/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. *//* * Permanent Certificate database handling code */#include"lowkeyti.h"#include"pcert.h"#include"mcom_db.h"#include"pcert.h"#include"secitem.h"#include"secder.h"#include"secerr.h"#include"lgdb.h"/* forward declaration */NSSLOWCERTCertificate*nsslowcert_FindCertByDERCertNoLocking(NSSLOWCERTCertDBHandle*handle,SECItem*derCert);staticSECStatusnsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle*dbhandle,char*emailAddr,SECItem*derSubject,SECItem*emailProfile,SECItem*profileTime);staticSECStatusnsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle*dbhandle,NSSLOWCERTCertificate*cert,char*nickname,NSSLOWCERTCertTrust*trust);staticSECStatusnsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle*handle,SECItem*derCrl,SECItem*crlKey,char*url,PRBoolisKRL);staticNSSLOWCERTCertificate*certListHead=NULL;staticNSSLOWCERTTrust*trustListHead=NULL;staticcertDBEntryCert*entryListHead=NULL;staticintcertListCount=0;staticinttrustListCount=0;staticintentryListCount=0;#define MAX_CERT_LIST_COUNT 10#define MAX_TRUST_LIST_COUNT 10#define MAX_ENTRY_LIST_COUNT 10/* * the following functions are wrappers for the db library that implement * a global lock to make the database thread safe. */staticPZLock*dbLock=NULL;staticPZLock*certRefCountLock=NULL;staticPZLock*certTrustLock=NULL;staticPZLock*freeListLock=NULL;voidcertdb_InitDBLock(NSSLOWCERTCertDBHandle*handle){if(dbLock==NULL){dbLock=PZ_NewLock(nssILockCertDB);PORT_Assert(dbLock!=NULL);}}SECStatusnsslowcert_InitLocks(void){if(freeListLock==NULL){freeListLock=PZ_NewLock(nssILockRefLock);if(freeListLock==NULL){returnSECFailure;}}if(certRefCountLock==NULL){certRefCountLock=PZ_NewLock(nssILockRefLock);if(certRefCountLock==NULL){returnSECFailure;}}if(certTrustLock==NULL){certTrustLock=PZ_NewLock(nssILockCertDB);if(certTrustLock==NULL){returnSECFailure;}}returnSECSuccess;}/* * Acquire the global lock on the cert database. * This lock is currently used for the following operations: * adding or deleting a cert to either the temp or perm databases * converting a temp to perm or perm to temp * changing (maybe just adding!?) the trust of a cert * chaning the DB status checking Configuration */staticvoidnsslowcert_LockDB(NSSLOWCERTCertDBHandle*handle){PZ_EnterMonitor(handle->dbMon);return;}/* * Free the global cert database lock. */staticvoidnsslowcert_UnlockDB(NSSLOWCERTCertDBHandle*handle){#ifdef DEBUGPRStatusprstat=PZ_ExitMonitor(handle->dbMon);PORT_Assert(prstat==PR_SUCCESS);#elsePZ_ExitMonitor(handle->dbMon);#endif}/* * Acquire the cert reference count lock * There is currently one global lock for all certs, but I'm putting a cert * arg here so that it will be easy to make it per-cert in the future if * that turns out to be necessary. */staticvoidnsslowcert_LockCertRefCount(NSSLOWCERTCertificate*cert){PORT_Assert(certRefCountLock!=NULL);PZ_Lock(certRefCountLock);return;}/* * Free the cert reference count lock */staticvoidnsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate*cert){PORT_Assert(certRefCountLock!=NULL);#ifdef DEBUG{PRStatusprstat=PZ_Unlock(certRefCountLock);PORT_Assert(prstat==PR_SUCCESS);}#elsePZ_Unlock(certRefCountLock);#endif}/* * Acquire the cert trust lock * There is currently one global lock for all certs, but I'm putting a cert * arg here so that it will be easy to make it per-cert in the future if * that turns out to be necessary. */staticvoidnsslowcert_LockCertTrust(NSSLOWCERTCertificate*cert){PORT_Assert(certTrustLock!=NULL);PZ_Lock(certTrustLock);return;}/* * Free the cert trust lock */staticvoidnsslowcert_UnlockCertTrust(NSSLOWCERTCertificate*cert){PORT_Assert(certTrustLock!=NULL);#ifdef DEBUG{PRStatusprstat=PZ_Unlock(certTrustLock);PORT_Assert(prstat==PR_SUCCESS);}#elsePZ_Unlock(certTrustLock);#endif}/* * Acquire the cert reference count lock * There is currently one global lock for all certs, but I'm putting a cert * arg here so that it will be easy to make it per-cert in the future if * that turns out to be necessary. */staticvoidnsslowcert_LockFreeList(void){PORT_Assert(freeListLock!=NULL);SKIP_AFTER_FORK(PZ_Lock(freeListLock));return;}/* * Free the cert reference count lock */staticvoidnsslowcert_UnlockFreeList(void){PORT_Assert(freeListLock!=NULL);#ifdef DEBUG{PRStatusprstat=PR_SUCCESS;SKIP_AFTER_FORK(prstat=PZ_Unlock(freeListLock));PORT_Assert(prstat==PR_SUCCESS);}#elseSKIP_AFTER_FORK(PZ_Unlock(freeListLock));#endif}NSSLOWCERTCertificate*nsslowcert_DupCertificate(NSSLOWCERTCertificate*c){if(c){nsslowcert_LockCertRefCount(c);++c->referenceCount;nsslowcert_UnlockCertRefCount(c);}returnc;}staticintcertdb_Get(DB*db,DBT*key,DBT*data,unsignedintflags){intret;PORT_Assert(dbLock!=NULL);PZ_Lock(dbLock);ret=(*db->get)(db,key,data,flags);(void)PZ_Unlock(dbLock);return(ret);}staticintcertdb_Put(DB*db,DBT*key,DBT*data,unsignedintflags){intret=0;PORT_Assert(dbLock!=NULL);PZ_Lock(dbLock);ret=(*db->put)(db,key,data,flags);(void)PZ_Unlock(dbLock);return(ret);}staticintcertdb_Sync(DB*db,unsignedintflags){intret;PORT_Assert(dbLock!=NULL);PZ_Lock(dbLock);ret=(*db->sync)(db,flags);(void)PZ_Unlock(dbLock);return(ret);}#define DB_NOT_FOUND -30991 /* from DBM 3.2 */staticintcertdb_Del(DB*db,DBT*key,unsignedintflags){intret;PORT_Assert(dbLock!=NULL);PZ_Lock(dbLock);ret=(*db->del)(db,key,flags);(void)PZ_Unlock(dbLock);/* don't fail if the record is already deleted */if(ret==DB_NOT_FOUND){ret=0;}return(ret);}staticintcertdb_Seq(DB*db,DBT*key,DBT*data,unsignedintflags){intret;PORT_Assert(dbLock!=NULL);PZ_Lock(dbLock);ret=(*db->seq)(db,key,data,flags);(void)PZ_Unlock(dbLock);return(ret);}staticvoidcertdb_Close(DB*db){PORT_Assert(dbLock!=NULL);SKIP_AFTER_FORK(PZ_Lock(dbLock));(*db->close)(db);SKIP_AFTER_FORK(PZ_Unlock(dbLock));return;}voidpkcs11_freeNickname(char*nickname,char*space){if(nickname&&nickname!=space){PORT_Free(nickname);}}char*pkcs11_copyNickname(char*nickname,char*space,intspaceLen){intlen;char*copy=NULL;len=PORT_Strlen(nickname)+1;if(len<=spaceLen){copy=space;PORT_Memcpy(copy,nickname,len);}else{copy=PORT_Strdup(nickname);}returncopy;}voidpkcs11_freeStaticData(unsignedchar*data,unsignedchar*space){if(data&&data!=space){PORT_Free(data);}}unsignedchar*pkcs11_allocStaticData(intlen,unsignedchar*space,intspaceLen){unsignedchar*data=NULL;if(len<=spaceLen){data=space;}else{data=(unsignedchar*)PORT_Alloc(len);}returndata;}unsignedchar*pkcs11_copyStaticData(unsignedchar*data,intlen,unsignedchar*space,intspaceLen){unsignedchar*copy=pkcs11_allocStaticData(len,space,spaceLen);if(copy){PORT_Memcpy(copy,data,len);}returncopy;}/* * destroy a database entry */staticvoidDestroyDBEntry(certDBEntry*entry){PLArenaPool*arena=entry->common.arena;/* must be one of our certDBEntry from the free list */if(arena==NULL){certDBEntryCert*certEntry;if(entry->common.type!=certDBEntryTypeCert){return;}certEntry=(certDBEntryCert*)entry;pkcs11_freeStaticData(certEntry->derCert.data,certEntry->derCertSpace);pkcs11_freeNickname(certEntry->nickname,certEntry->nicknameSpace);nsslowcert_LockFreeList();if(entryListCount>MAX_ENTRY_LIST_COUNT){PORT_Free(certEntry);}else{entryListCount++;PORT_Memset(certEntry,0,sizeof(*certEntry));certEntry->next=entryListHead;entryListHead=certEntry;}nsslowcert_UnlockFreeList();return;}/* Zero out the entry struct, so that any further attempts to use it * will cause an exception (e.g. null pointer reference). */PORT_Memset(&entry->common,0,sizeofentry->common);PORT_FreeArena(arena,PR_FALSE);return;}/* forward references */staticvoidnsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate*cert);staticSECStatusDeleteDBEntry(NSSLOWCERTCertDBHandle*handle,certDBEntryTypetype,SECItem*dbkey){DBTkey;intret;/* init the database key */key.data=dbkey->data;key.size=dbkey->len;dbkey->data[0]=(unsignedchar)type;/* delete entry from database */ret=certdb_Del(handle->permCertDB,&key,0);if(ret!=0){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}ret=certdb_Sync(handle->permCertDB,0);if(ret){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}return(SECSuccess);loser:return(SECFailure);}staticSECStatusReadDBEntry(NSSLOWCERTCertDBHandle*handle,certDBEntryCommon*entry,SECItem*dbkey,SECItem*dbentry,PLArenaPool*arena){DBTdata,key;intret;unsignedchar*buf;/* init the database key */key.data=dbkey->data;key.size=dbkey->len;dbkey->data[0]=(unsignedchar)entry->type;/* read entry from database */ret=certdb_Get(handle->permCertDB,&key,&data,0);if(ret!=0){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* validate the entry */if(data.size<SEC_DB_ENTRY_HEADER_LEN){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}buf=(unsignedchar*)data.data;/* version 7 has the same schema, we may be using a v7 db if we openned * the databases readonly. */if(!((buf[0]==(unsignedchar)CERT_DB_FILE_VERSION)||(buf[0]==(unsignedchar)CERT_DB_V7_FILE_VERSION))){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}if(buf[1]!=(unsignedchar)entry->type){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* copy out header information */entry->version=(unsignedint)buf[0];entry->type=(certDBEntryType)buf[1];entry->flags=(unsignedint)buf[2];/* format body of entry for return to caller */dbentry->len=data.size-SEC_DB_ENTRY_HEADER_LEN;if(dbentry->len){if(arena){dbentry->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbentry->len);if(dbentry->data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(dbentry->data,&buf[SEC_DB_ENTRY_HEADER_LEN],dbentry->len);}else{dbentry->data=&buf[SEC_DB_ENTRY_HEADER_LEN];}}else{dbentry->data=NULL;}return(SECSuccess);loser:return(SECFailure);}/** ** Implement low level database access **/staticSECStatusWriteDBEntry(NSSLOWCERTCertDBHandle*handle,certDBEntryCommon*entry,SECItem*dbkey,SECItem*dbentry){intret;DBTdata,key;unsignedchar*buf;data.data=dbentry->data;data.size=dbentry->len;buf=(unsignedchar*)data.data;buf[0]=(unsignedchar)entry->version;buf[1]=(unsignedchar)entry->type;buf[2]=(unsignedchar)entry->flags;key.data=dbkey->data;key.size=dbkey->len;dbkey->data[0]=(unsignedchar)entry->type;/* put the record into the database now */ret=certdb_Put(handle->permCertDB,&key,&data,0);if(ret!=0){gotoloser;}ret=certdb_Sync(handle->permCertDB,0);if(ret){gotoloser;}return(SECSuccess);loser:return(SECFailure);}/* * encode a database cert record */staticSECStatusEncodeDBCertEntry(certDBEntryCert*entry,PLArenaPool*arena,SECItem*dbitem){unsignedintnnlen;unsignedchar*buf;char*nn;charzbuf=0;if(entry->nickname){nn=entry->nickname;}else{nn=&zbuf;}nnlen=PORT_Strlen(nn)+1;/* allocate space for encoded database record, including space * for low level header */dbitem->len=entry->derCert.len+nnlen+DB_CERT_ENTRY_HEADER_LEN+SEC_DB_ENTRY_HEADER_LEN;dbitem->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbitem->len);if(dbitem->data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}/* fill in database record */buf=&dbitem->data[SEC_DB_ENTRY_HEADER_LEN];buf[0]=(PRUint8)(entry->trust.sslFlags>>8);buf[1]=(PRUint8)(entry->trust.sslFlags);buf[2]=(PRUint8)(entry->trust.emailFlags>>8);buf[3]=(PRUint8)(entry->trust.emailFlags);buf[4]=(PRUint8)(entry->trust.objectSigningFlags>>8);buf[5]=(PRUint8)(entry->trust.objectSigningFlags);buf[6]=(PRUint8)(entry->derCert.len>>8);buf[7]=(PRUint8)(entry->derCert.len);buf[8]=(PRUint8)(nnlen>>8);buf[9]=(PRUint8)(nnlen);PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN],entry->derCert.data,entry->derCert.len);PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN+entry->derCert.len],nn,nnlen);return(SECSuccess);loser:return(SECFailure);}/* * encode a database key for a cert record */staticSECStatusEncodeDBCertKey(constSECItem*certKey,PLArenaPool*arena,SECItem*dbkey){unsignedintlen=certKey->len+SEC_DB_KEY_HEADER_LEN;if(len>NSS_MAX_LEGACY_DB_KEY_SIZE)gotoloser;if(arena){dbkey->data=(unsignedchar*)PORT_ArenaAlloc(arena,len);}else{if(dbkey->len<len){dbkey->data=(unsignedchar*)PORT_Alloc(len);}}dbkey->len=len;if(dbkey->data==NULL){gotoloser;}PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],certKey->data,certKey->len);dbkey->data[0]=certDBEntryTypeCert;return(SECSuccess);loser:return(SECFailure);}staticSECStatusEncodeDBGenericKey(constSECItem*certKey,PLArenaPool*arena,SECItem*dbkey,certDBEntryTypeentryType){/* * we only allow _one_ KRL key! */if(entryType==certDBEntryTypeKeyRevocation){dbkey->len=SEC_DB_KEY_HEADER_LEN;dbkey->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbkey->len);if(dbkey->data==NULL){gotoloser;}dbkey->data[0]=(unsignedchar)entryType;return(SECSuccess);}dbkey->len=certKey->len+SEC_DB_KEY_HEADER_LEN;if(dbkey->len>NSS_MAX_LEGACY_DB_KEY_SIZE)gotoloser;dbkey->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbkey->len);if(dbkey->data==NULL){gotoloser;}PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],certKey->data,certKey->len);dbkey->data[0]=(unsignedchar)entryType;return(SECSuccess);loser:return(SECFailure);}staticSECStatusDecodeDBCertEntry(certDBEntryCert*entry,SECItem*dbentry){unsignedintnnlen;unsignedintheaderlen;intlenoff;/* allow updates of old versions of the database */switch(entry->common.version){case5:headerlen=DB_CERT_V5_ENTRY_HEADER_LEN;lenoff=3;break;case6:/* should not get here */PORT_Assert(0);headerlen=DB_CERT_V6_ENTRY_HEADER_LEN;lenoff=3;break;case7:case8:headerlen=DB_CERT_ENTRY_HEADER_LEN;lenoff=6;break;default:/* better not get here */PORT_Assert(0);headerlen=DB_CERT_V5_ENTRY_HEADER_LEN;lenoff=3;break;}/* is record long enough for header? */if(dbentry->len<headerlen){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* is database entry correct length? */entry->derCert.len=((dbentry->data[lenoff]<<8)|dbentry->data[lenoff+1]);nnlen=((dbentry->data[lenoff+2]<<8)|dbentry->data[lenoff+3]);lenoff=dbentry->len-(entry->derCert.len+nnlen+headerlen);if(lenoff){if(lenoff<0||(lenoff&0xffff)!=0){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* The cert size exceeded 64KB. Reconstruct the correct length. */entry->derCert.len+=lenoff;}/* Is data long enough? */if(dbentry->len<headerlen+entry->derCert.len){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* copy the dercert */entry->derCert.data=pkcs11_copyStaticData(&dbentry->data[headerlen],entry->derCert.len,entry->derCertSpace,sizeof(entry->derCertSpace));if(entry->derCert.data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}/* copy the nickname */if(nnlen>1){/* Is data long enough? */if(dbentry->len<headerlen+entry->derCert.len+nnlen){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}entry->nickname=(char*)pkcs11_copyStaticData(&dbentry->data[headerlen+entry->derCert.len],nnlen,(unsignedchar*)entry->nicknameSpace,sizeof(entry->nicknameSpace));if(entry->nickname==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}}else{entry->nickname=NULL;}if(entry->common.version<7){/* allow updates of v5 db */entry->trust.sslFlags=dbentry->data[0];entry->trust.emailFlags=dbentry->data[1];entry->trust.objectSigningFlags=dbentry->data[2];}else{entry->trust.sslFlags=(dbentry->data[0]<<8)|dbentry->data[1];entry->trust.emailFlags=(dbentry->data[2]<<8)|dbentry->data[3];entry->trust.objectSigningFlags=(dbentry->data[4]<<8)|dbentry->data[5];}return(SECSuccess);loser:return(SECFailure);}/* * Create a new certDBEntryCert from existing data */staticcertDBEntryCert*NewDBCertEntry(SECItem*derCert,char*nickname,NSSLOWCERTCertTrust*trust,intflags){certDBEntryCert*entry;PLArenaPool*arena=NULL;intnnlen;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(!arena){gotoloser;}entry=PORT_ArenaZNew(arena,certDBEntryCert);if(entry==NULL){gotoloser;}/* fill in the dbCert */entry->common.arena=arena;entry->common.type=certDBEntryTypeCert;entry->common.version=CERT_DB_FILE_VERSION;entry->common.flags=flags;if(trust){entry->trust=*trust;}entry->derCert.data=(unsignedchar*)PORT_ArenaAlloc(arena,derCert->len);if(!entry->derCert.data){gotoloser;}entry->derCert.len=derCert->len;PORT_Memcpy(entry->derCert.data,derCert->data,derCert->len);nnlen=(nickname?strlen(nickname)+1:0);if(nnlen){entry->nickname=(char*)PORT_ArenaAlloc(arena,nnlen);if(!entry->nickname){gotoloser;}PORT_Memcpy(entry->nickname,nickname,nnlen);}else{entry->nickname=0;}return(entry);loser:/* allocation error, free arena and return */if(arena){PORT_FreeArena(arena,PR_FALSE);}PORT_SetError(SEC_ERROR_NO_MEMORY);return(0);}/* * Decode a version 4 DBCert from the byte stream database format * and construct a current database entry struct */staticcertDBEntryCert*DecodeV4DBCertEntry(unsignedchar*buf,intlen){certDBEntryCert*entry;intcertlen;intnnlen;PLArenaPool*arena;/* make sure length is at least long enough for the header */if(len<DBCERT_V4_HEADER_LEN){PORT_SetError(SEC_ERROR_BAD_DATABASE);return(0);}/* get other lengths */certlen=buf[3]<<8|buf[4];nnlen=buf[5]<<8|buf[6];/* make sure DB entry is the right size */if((certlen+nnlen+DBCERT_V4_HEADER_LEN)!=len){PORT_SetError(SEC_ERROR_BAD_DATABASE);return(0);}/* allocate arena */arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(!arena){PORT_SetError(SEC_ERROR_NO_MEMORY);return(0);}/* allocate structure and members */entry=(certDBEntryCert*)PORT_ArenaAlloc(arena,sizeof(certDBEntryCert));if(!entry){gotoloser;}entry->common.arena=arena;entry->common.version=CERT_DB_FILE_VERSION;entry->common.type=certDBEntryTypeCert;entry->common.flags=0;entry->trust.sslFlags=buf[0];entry->trust.emailFlags=buf[1];entry->trust.objectSigningFlags=buf[2];entry->derCert.data=(unsignedchar*)PORT_ArenaAlloc(arena,certlen);if(!entry->derCert.data){gotoloser;}entry->derCert.len=certlen;PORT_Memcpy(entry->derCert.data,&buf[DBCERT_V4_HEADER_LEN],certlen);if(nnlen){entry->nickname=(char*)PORT_ArenaAlloc(arena,nnlen);if(!entry->nickname){gotoloser;}PORT_Memcpy(entry->nickname,&buf[DBCERT_V4_HEADER_LEN+certlen],nnlen);if(PORT_Strcmp(entry->nickname,"Server-Cert")==0){entry->trust.sslFlags|=CERTDB_USER;}}else{entry->nickname=0;}return(entry);loser:PORT_FreeArena(arena,PR_FALSE);PORT_SetError(SEC_ERROR_NO_MEMORY);return(0);}/* * Encode a Certificate database entry into byte stream suitable for * the database */staticSECStatusWriteDBCertEntry(NSSLOWCERTCertDBHandle*handle,certDBEntryCert*entry){SECItemdbitem,dbkey;PLArenaPool*tmparena=NULL;SECItemtmpitem;SECStatusrv;tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){gotoloser;}rv=EncodeDBCertEntry(entry,tmparena,&dbitem);if(rv!=SECSuccess){gotoloser;}/* get the database key and format it */rv=nsslowcert_KeyFromDERCert(tmparena,&entry->derCert,&tmpitem);if(rv==SECFailure){gotoloser;}rv=EncodeDBCertKey(&tmpitem,tmparena,&dbkey);if(rv==SECFailure){gotoloser;}/* now write it to the database */rv=WriteDBEntry(handle,&entry->common,&dbkey,&dbitem);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(SECSuccess);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}return(SECFailure);}/* * delete a certificate entry */staticSECStatusDeleteDBCertEntry(NSSLOWCERTCertDBHandle*handle,SECItem*certKey){SECItemdbkey;SECStatusrv;dbkey.data=NULL;dbkey.len=0;rv=EncodeDBCertKey(certKey,NULL,&dbkey);if(rv!=SECSuccess){gotoloser;}rv=DeleteDBEntry(handle,certDBEntryTypeCert,&dbkey);if(rv==SECFailure){gotoloser;}PORT_Free(dbkey.data);return(SECSuccess);loser:if(dbkey.data){PORT_Free(dbkey.data);}return(SECFailure);}staticcertDBEntryCert*CreateCertEntry(void){certDBEntryCert*entry;nsslowcert_LockFreeList();entry=entryListHead;if(entry){entryListCount--;entryListHead=entry->next;}PORT_Assert(entryListCount>=0);nsslowcert_UnlockFreeList();if(entry){returnentry;}returnPORT_ZNew(certDBEntryCert);}staticvoidDestroyCertEntryFreeList(void){certDBEntryCert*entry;nsslowcert_LockFreeList();while(NULL!=(entry=entryListHead)){entryListCount--;entryListHead=entry->next;PORT_Free(entry);}PORT_Assert(!entryListCount);entryListCount=0;nsslowcert_UnlockFreeList();}/* * Read a certificate entry */staticcertDBEntryCert*ReadDBCertEntry(NSSLOWCERTCertDBHandle*handle,constSECItem*certKey){certDBEntryCert*entry;SECItemdbkey;SECItemdbentry;SECStatusrv;unsignedcharbuf[512];dbkey.data=buf;dbkey.len=sizeof(buf);entry=CreateCertEntry();if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry->common.arena=NULL;entry->common.type=certDBEntryTypeCert;rv=EncodeDBCertKey(certKey,NULL,&dbkey);if(rv!=SECSuccess){gotoloser;}rv=ReadDBEntry(handle,&entry->common,&dbkey,&dbentry,NULL);if(rv==SECFailure){gotoloser;}rv=DecodeDBCertEntry(entry,&dbentry);if(rv!=SECSuccess){gotoloser;}pkcs11_freeStaticData(dbkey.data,buf);dbkey.data=NULL;return(entry);loser:pkcs11_freeStaticData(dbkey.data,buf);dbkey.data=NULL;if(entry){DestroyDBEntry((certDBEntry*)entry);}return(NULL);}/* * encode a database cert record */staticSECStatusEncodeDBCrlEntry(certDBEntryRevocation*entry,PLArenaPool*arena,SECItem*dbitem){unsignedintnnlen=0;unsignedchar*buf;if(entry->url){nnlen=PORT_Strlen(entry->url)+1;}/* allocate space for encoded database record, including space * for low level header */dbitem->len=entry->derCrl.len+nnlen+SEC_DB_ENTRY_HEADER_LEN+DB_CRL_ENTRY_HEADER_LEN;dbitem->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbitem->len);if(dbitem->data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}/* fill in database record */buf=&dbitem->data[SEC_DB_ENTRY_HEADER_LEN];buf[0]=(PRUint8)(entry->derCrl.len>>8);buf[1]=(PRUint8)(entry->derCrl.len);buf[2]=(PRUint8)(nnlen>>8);buf[3]=(PRUint8)(nnlen);PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN],entry->derCrl.data,entry->derCrl.len);if(nnlen!=0){PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN+entry->derCrl.len],entry->url,nnlen);}return(SECSuccess);loser:return(SECFailure);}staticSECStatusDecodeDBCrlEntry(certDBEntryRevocation*entry,SECItem*dbentry){unsignedinturlLen;intlenDiff;/* is record long enough for header? */if(dbentry->len<DB_CRL_ENTRY_HEADER_LEN){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* is database entry correct length? */entry->derCrl.len=((dbentry->data[0]<<8)|dbentry->data[1]);urlLen=((dbentry->data[2]<<8)|dbentry->data[3]);lenDiff=dbentry->len-(entry->derCrl.len+urlLen+DB_CRL_ENTRY_HEADER_LEN);if(lenDiff){if(lenDiff<0||(lenDiff&0xffff)!=0){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* CRL entry is greater than 64 K. Hack to make this continue to work */entry->derCrl.len+=lenDiff;}/* copy the der CRL */entry->derCrl.data=(unsignedchar*)PORT_ArenaAlloc(entry->common.arena,entry->derCrl.len);if(entry->derCrl.data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->derCrl.data,&dbentry->data[DB_CRL_ENTRY_HEADER_LEN],entry->derCrl.len);/* copy the url */entry->url=NULL;if(urlLen!=0){entry->url=(char*)PORT_ArenaAlloc(entry->common.arena,urlLen);if(entry->url==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->url,&dbentry->data[DB_CRL_ENTRY_HEADER_LEN+entry->derCrl.len],urlLen);}return(SECSuccess);loser:return(SECFailure);}/* * Create a new certDBEntryRevocation from existing data */staticcertDBEntryRevocation*NewDBCrlEntry(SECItem*derCrl,char*url,certDBEntryTypecrlType,intflags){certDBEntryRevocation*entry;PLArenaPool*arena=NULL;intnnlen;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(!arena){gotoloser;}entry=PORT_ArenaZNew(arena,certDBEntryRevocation);if(entry==NULL){gotoloser;}/* fill in the dbRevolcation */entry->common.arena=arena;entry->common.type=crlType;entry->common.version=CERT_DB_FILE_VERSION;entry->common.flags=flags;entry->derCrl.data=(unsignedchar*)PORT_ArenaAlloc(arena,derCrl->len);if(!entry->derCrl.data){gotoloser;}if(url){nnlen=PORT_Strlen(url)+1;entry->url=(char*)PORT_ArenaAlloc(arena,nnlen);if(!entry->url){gotoloser;}PORT_Memcpy(entry->url,url,nnlen);}else{entry->url=NULL;}entry->derCrl.len=derCrl->len;PORT_Memcpy(entry->derCrl.data,derCrl->data,derCrl->len);return(entry);loser:/* allocation error, free arena and return */if(arena){PORT_FreeArena(arena,PR_FALSE);}PORT_SetError(SEC_ERROR_NO_MEMORY);return(0);}staticSECStatusWriteDBCrlEntry(NSSLOWCERTCertDBHandle*handle,certDBEntryRevocation*entry,SECItem*crlKey){SECItemdbkey;PLArenaPool*tmparena=NULL;SECItemencodedEntry;SECStatusrv;tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){gotoloser;}rv=EncodeDBCrlEntry(entry,tmparena,&encodedEntry);if(rv==SECFailure){gotoloser;}rv=EncodeDBGenericKey(crlKey,tmparena,&dbkey,entry->common.type);if(rv==SECFailure){gotoloser;}/* now write it to the database */rv=WriteDBEntry(handle,&entry->common,&dbkey,&encodedEntry);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(SECSuccess);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}return(SECFailure);}/* * delete a crl entry */staticSECStatusDeleteDBCrlEntry(NSSLOWCERTCertDBHandle*handle,constSECItem*crlKey,certDBEntryTypecrlType){SECItemdbkey;PLArenaPool*arena=NULL;SECStatusrv;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){gotoloser;}rv=EncodeDBGenericKey(crlKey,arena,&dbkey,crlType);if(rv!=SECSuccess){gotoloser;}rv=DeleteDBEntry(handle,crlType,&dbkey);if(rv==SECFailure){gotoloser;}PORT_FreeArena(arena,PR_FALSE);return(SECSuccess);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(SECFailure);}/* * Read a certificate entry */staticcertDBEntryRevocation*ReadDBCrlEntry(NSSLOWCERTCertDBHandle*handle,SECItem*certKey,certDBEntryTypecrlType){PLArenaPool*arena=NULL;PLArenaPool*tmparena=NULL;certDBEntryRevocation*entry;SECItemdbkey;SECItemdbentry;SECStatusrv;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry=(certDBEntryRevocation*)PORT_ArenaAlloc(arena,sizeof(certDBEntryRevocation));if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry->common.arena=arena;entry->common.type=crlType;rv=EncodeDBGenericKey(certKey,tmparena,&dbkey,crlType);if(rv!=SECSuccess){gotoloser;}rv=ReadDBEntry(handle,&entry->common,&dbkey,&dbentry,NULL);if(rv==SECFailure){gotoloser;}rv=DecodeDBCrlEntry(entry,&dbentry);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(entry);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}if(arena){PORT_FreeArena(arena,PR_FALSE);}return(NULL);}voidnsslowcert_DestroyDBEntry(certDBEntry*entry){DestroyDBEntry(entry);return;}/* * Encode a database nickname record */staticSECStatusEncodeDBNicknameEntry(certDBEntryNickname*entry,PLArenaPool*arena,SECItem*dbitem){unsignedchar*buf;/* allocate space for encoded database record, including space * for low level header */dbitem->len=entry->subjectName.len+DB_NICKNAME_ENTRY_HEADER_LEN+SEC_DB_ENTRY_HEADER_LEN;dbitem->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbitem->len);if(dbitem->data==NULL){gotoloser;}/* fill in database record */buf=&dbitem->data[SEC_DB_ENTRY_HEADER_LEN];buf[0]=(PRUint8)(entry->subjectName.len>>8);buf[1]=(PRUint8)(entry->subjectName.len);PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN],entry->subjectName.data,entry->subjectName.len);return(SECSuccess);loser:return(SECFailure);}/* * Encode a database key for a nickname record */staticSECStatusEncodeDBNicknameKey(char*nickname,PLArenaPool*arena,SECItem*dbkey){unsignedintnnlen;nnlen=PORT_Strlen(nickname)+1;/* includes null *//* now get the database key and format it */dbkey->len=nnlen+SEC_DB_KEY_HEADER_LEN;if(dbkey->len>NSS_MAX_LEGACY_DB_KEY_SIZE)gotoloser;dbkey->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbkey->len);if(dbkey->data==NULL){gotoloser;}PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],nickname,nnlen);dbkey->data[0]=certDBEntryTypeNickname;return(SECSuccess);loser:return(SECFailure);}staticSECStatusDecodeDBNicknameEntry(certDBEntryNickname*entry,SECItem*dbentry,char*nickname){intlenDiff;/* is record long enough for header? */if(dbentry->len<DB_NICKNAME_ENTRY_HEADER_LEN){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* is database entry correct length? */entry->subjectName.len=((dbentry->data[0]<<8)|dbentry->data[1]);lenDiff=dbentry->len-(entry->subjectName.len+DB_NICKNAME_ENTRY_HEADER_LEN);if(lenDiff){if(lenDiff<0||(lenDiff&0xffff)!=0){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* The entry size exceeded 64KB. Reconstruct the correct length. */entry->subjectName.len+=lenDiff;}/* copy the certkey */entry->subjectName.data=(unsignedchar*)PORT_ArenaAlloc(entry->common.arena,entry->subjectName.len);if(entry->subjectName.data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->subjectName.data,&dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],entry->subjectName.len);entry->subjectName.type=siBuffer;entry->nickname=(char*)PORT_ArenaAlloc(entry->common.arena,PORT_Strlen(nickname)+1);if(entry->nickname){PORT_Strcpy(entry->nickname,nickname);}return(SECSuccess);loser:return(SECFailure);}/* * create a new nickname entry */staticcertDBEntryNickname*NewDBNicknameEntry(char*nickname,SECItem*subjectName,unsignedintflags){PLArenaPool*arena=NULL;certDBEntryNickname*entry;intnnlen;SECStatusrv;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry=(certDBEntryNickname*)PORT_ArenaAlloc(arena,sizeof(certDBEntryNickname));if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}/* init common fields */entry->common.arena=arena;entry->common.type=certDBEntryTypeNickname;entry->common.version=CERT_DB_FILE_VERSION;entry->common.flags=flags;/* copy the nickname */nnlen=PORT_Strlen(nickname)+1;entry->nickname=(char*)PORT_ArenaAlloc(arena,nnlen);if(entry->nickname==NULL){gotoloser;}PORT_Memcpy(entry->nickname,nickname,nnlen);rv=SECITEM_CopyItem(arena,&entry->subjectName,subjectName);if(rv!=SECSuccess){gotoloser;}return(entry);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(NULL);}/* * delete a nickname entry */staticSECStatusDeleteDBNicknameEntry(NSSLOWCERTCertDBHandle*handle,char*nickname){PLArenaPool*arena=NULL;SECStatusrv;SECItemdbkey;if(nickname==NULL){return(SECSuccess);}arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){gotoloser;}rv=EncodeDBNicknameKey(nickname,arena,&dbkey);if(rv!=SECSuccess){gotoloser;}rv=DeleteDBEntry(handle,certDBEntryTypeNickname,&dbkey);if(rv==SECFailure){gotoloser;}PORT_FreeArena(arena,PR_FALSE);return(SECSuccess);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(SECFailure);}/* * Read a nickname entry */staticcertDBEntryNickname*ReadDBNicknameEntry(NSSLOWCERTCertDBHandle*handle,char*nickname){PLArenaPool*arena=NULL;PLArenaPool*tmparena=NULL;certDBEntryNickname*entry;SECItemdbkey;SECItemdbentry;SECStatusrv;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry=(certDBEntryNickname*)PORT_ArenaAlloc(arena,sizeof(certDBEntryNickname));if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry->common.arena=arena;entry->common.type=certDBEntryTypeNickname;rv=EncodeDBNicknameKey(nickname,tmparena,&dbkey);if(rv!=SECSuccess){gotoloser;}rv=ReadDBEntry(handle,&entry->common,&dbkey,&dbentry,tmparena);if(rv==SECFailure){gotoloser;}/* is record long enough for header? */if(dbentry.len<DB_NICKNAME_ENTRY_HEADER_LEN){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}rv=DecodeDBNicknameEntry(entry,&dbentry,nickname);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(entry);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}if(arena){PORT_FreeArena(arena,PR_FALSE);}return(NULL);}/* * Encode a nickname entry into byte stream suitable for * the database */staticSECStatusWriteDBNicknameEntry(NSSLOWCERTCertDBHandle*handle,certDBEntryNickname*entry){SECItemdbitem,dbkey;PLArenaPool*tmparena=NULL;SECStatusrv;tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){gotoloser;}rv=EncodeDBNicknameEntry(entry,tmparena,&dbitem);if(rv!=SECSuccess){gotoloser;}rv=EncodeDBNicknameKey(entry->nickname,tmparena,&dbkey);if(rv!=SECSuccess){gotoloser;}/* now write it to the database */rv=WriteDBEntry(handle,&entry->common,&dbkey,&dbitem);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(SECSuccess);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}return(SECFailure);}staticSECStatusEncodeDBSMimeEntry(certDBEntrySMime*entry,PLArenaPool*arena,SECItem*dbitem){unsignedchar*buf;/* allocate space for encoded database record, including space * for low level header */dbitem->len=entry->subjectName.len+entry->smimeOptions.len+entry->optionsDate.len+DB_SMIME_ENTRY_HEADER_LEN+SEC_DB_ENTRY_HEADER_LEN;dbitem->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbitem->len);if(dbitem->data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}/* fill in database record */buf=&dbitem->data[SEC_DB_ENTRY_HEADER_LEN];buf[0]=(PRUint8)(entry->subjectName.len>>8);buf[1]=(PRUint8)(entry->subjectName.len);buf[2]=(PRUint8)(entry->smimeOptions.len>>8);buf[3]=(PRUint8)(entry->smimeOptions.len);buf[4]=(PRUint8)(entry->optionsDate.len>>8);buf[5]=(PRUint8)(entry->optionsDate.len);/* if no smime options, then there should not be an options date either */PORT_Assert(!((entry->smimeOptions.len==0)&&(entry->optionsDate.len!=0)));PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN],entry->subjectName.data,entry->subjectName.len);if(entry->smimeOptions.len){PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN+entry->subjectName.len],entry->smimeOptions.data,entry->smimeOptions.len);PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN+entry->subjectName.len+entry->smimeOptions.len],entry->optionsDate.data,entry->optionsDate.len);}return(SECSuccess);loser:return(SECFailure);}/* * Encode a database key for a SMIME record */staticSECStatusEncodeDBSMimeKey(char*emailAddr,PLArenaPool*arena,SECItem*dbkey){unsignedintaddrlen;addrlen=PORT_Strlen(emailAddr)+1;/* includes null *//* now get the database key and format it */dbkey->len=addrlen+SEC_DB_KEY_HEADER_LEN;if(dbkey->len>NSS_MAX_LEGACY_DB_KEY_SIZE)gotoloser;dbkey->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbkey->len);if(dbkey->data==NULL){gotoloser;}PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],emailAddr,addrlen);dbkey->data[0]=certDBEntryTypeSMimeProfile;return(SECSuccess);loser:return(SECFailure);}/* * Decode a database SMIME record */staticSECStatusDecodeDBSMimeEntry(certDBEntrySMime*entry,SECItem*dbentry,char*emailAddr){intlenDiff;/* is record long enough for header? */if(dbentry->len<DB_SMIME_ENTRY_HEADER_LEN){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* is database entry correct length? */entry->subjectName.len=((dbentry->data[0]<<8)|dbentry->data[1]);entry->smimeOptions.len=((dbentry->data[2]<<8)|dbentry->data[3]);entry->optionsDate.len=((dbentry->data[4]<<8)|dbentry->data[5]);lenDiff=dbentry->len-(entry->subjectName.len+entry->smimeOptions.len+entry->optionsDate.len+DB_SMIME_ENTRY_HEADER_LEN);if(lenDiff){if(lenDiff<0||(lenDiff&0xffff)!=0){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}/* The entry size exceeded 64KB. Reconstruct the correct length. */entry->subjectName.len+=lenDiff;}/* copy the subject name */entry->subjectName.data=(unsignedchar*)PORT_ArenaAlloc(entry->common.arena,entry->subjectName.len);if(entry->subjectName.data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->subjectName.data,&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],entry->subjectName.len);/* copy the smime options */if(entry->smimeOptions.len){entry->smimeOptions.data=(unsignedchar*)PORT_ArenaAlloc(entry->common.arena,entry->smimeOptions.len);if(entry->smimeOptions.data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->smimeOptions.data,&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN+entry->subjectName.len],entry->smimeOptions.len);}if(entry->optionsDate.len){entry->optionsDate.data=(unsignedchar*)PORT_ArenaAlloc(entry->common.arena,entry->optionsDate.len);if(entry->optionsDate.data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->optionsDate.data,&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN+entry->subjectName.len+entry->smimeOptions.len],entry->optionsDate.len);}/* both options and options date must either exist or not exist */if(((entry->optionsDate.len==0)||(entry->smimeOptions.len==0))&&entry->smimeOptions.len!=entry->optionsDate.len){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}entry->emailAddr=(char*)PORT_ArenaAlloc(entry->common.arena,PORT_Strlen(emailAddr)+1);if(entry->emailAddr){PORT_Strcpy(entry->emailAddr,emailAddr);}return(SECSuccess);loser:return(SECFailure);}/* * create a new SMIME entry */staticcertDBEntrySMime*NewDBSMimeEntry(char*emailAddr,SECItem*subjectName,SECItem*smimeOptions,SECItem*optionsDate,unsignedintflags){PLArenaPool*arena=NULL;certDBEntrySMime*entry;intaddrlen;SECStatusrv;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry=(certDBEntrySMime*)PORT_ArenaAlloc(arena,sizeof(certDBEntrySMime));if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}/* init common fields */entry->common.arena=arena;entry->common.type=certDBEntryTypeSMimeProfile;entry->common.version=CERT_DB_FILE_VERSION;entry->common.flags=flags;/* copy the email addr */addrlen=PORT_Strlen(emailAddr)+1;entry->emailAddr=(char*)PORT_ArenaAlloc(arena,addrlen);if(entry->emailAddr==NULL){gotoloser;}PORT_Memcpy(entry->emailAddr,emailAddr,addrlen);/* copy the subject name */rv=SECITEM_CopyItem(arena,&entry->subjectName,subjectName);if(rv!=SECSuccess){gotoloser;}/* copy the smime options */if(smimeOptions){rv=SECITEM_CopyItem(arena,&entry->smimeOptions,smimeOptions);if(rv!=SECSuccess){gotoloser;}}else{PORT_Assert(optionsDate==NULL);entry->smimeOptions.data=NULL;entry->smimeOptions.len=0;}/* copy the options date */if(optionsDate){rv=SECITEM_CopyItem(arena,&entry->optionsDate,optionsDate);if(rv!=SECSuccess){gotoloser;}}else{PORT_Assert(smimeOptions==NULL);entry->optionsDate.data=NULL;entry->optionsDate.len=0;}return(entry);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(NULL);}/* * delete a SMIME entry */staticSECStatusDeleteDBSMimeEntry(NSSLOWCERTCertDBHandle*handle,char*emailAddr){PLArenaPool*arena=NULL;SECStatusrv;SECItemdbkey;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){gotoloser;}rv=EncodeDBSMimeKey(emailAddr,arena,&dbkey);if(rv!=SECSuccess){gotoloser;}rv=DeleteDBEntry(handle,certDBEntryTypeSMimeProfile,&dbkey);if(rv==SECFailure){gotoloser;}PORT_FreeArena(arena,PR_FALSE);return(SECSuccess);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(SECFailure);}/* * Read a SMIME entry */certDBEntrySMime*nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle*handle,char*emailAddr){PLArenaPool*arena=NULL;PLArenaPool*tmparena=NULL;certDBEntrySMime*entry;SECItemdbkey;SECItemdbentry;SECStatusrv;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry=(certDBEntrySMime*)PORT_ArenaAlloc(arena,sizeof(certDBEntrySMime));if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry->common.arena=arena;entry->common.type=certDBEntryTypeSMimeProfile;rv=EncodeDBSMimeKey(emailAddr,tmparena,&dbkey);if(rv!=SECSuccess){gotoloser;}rv=ReadDBEntry(handle,&entry->common,&dbkey,&dbentry,tmparena);if(rv==SECFailure){gotoloser;}/* is record long enough for header? */if(dbentry.len<DB_SMIME_ENTRY_HEADER_LEN){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}rv=DecodeDBSMimeEntry(entry,&dbentry,emailAddr);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(entry);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}if(arena){PORT_FreeArena(arena,PR_FALSE);}return(NULL);}/* * Encode a SMIME entry into byte stream suitable for * the database */staticSECStatusWriteDBSMimeEntry(NSSLOWCERTCertDBHandle*handle,certDBEntrySMime*entry){SECItemdbitem,dbkey;PLArenaPool*tmparena=NULL;SECStatusrv;tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){gotoloser;}rv=EncodeDBSMimeEntry(entry,tmparena,&dbitem);if(rv!=SECSuccess){gotoloser;}rv=EncodeDBSMimeKey(entry->emailAddr,tmparena,&dbkey);if(rv!=SECSuccess){gotoloser;}/* now write it to the database */rv=WriteDBEntry(handle,&entry->common,&dbkey,&dbitem);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(SECSuccess);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}return(SECFailure);}/* * Encode a database subject record */staticSECStatusEncodeDBSubjectEntry(certDBEntrySubject*entry,PLArenaPool*arena,SECItem*dbitem){unsignedchar*buf;intlen;unsignedintncerts;unsignedinti;unsignedchar*tmpbuf;unsignedintnnlen=0;unsignedinteaddrslen=0;intkeyidoff;SECItem*certKeys=entry->certKeys;SECItem*keyIDs=entry->keyIDs;;if(entry->nickname){nnlen=PORT_Strlen(entry->nickname)+1;}if(entry->emailAddrs){eaddrslen=2;for(i=0;i<entry->nemailAddrs;i++){eaddrslen+=PORT_Strlen(entry->emailAddrs[i])+1+2;}}ncerts=entry->ncerts;/* compute the length of the entry */keyidoff=DB_SUBJECT_ENTRY_HEADER_LEN+nnlen;len=keyidoff+(4*ncerts)+eaddrslen;for(i=0;i<ncerts;i++){if(keyIDs[i].len>0xffff||(certKeys[i].len>0xffff)){PORT_SetError(SEC_ERROR_INPUT_LEN);gotoloser;}len+=certKeys[i].len;len+=keyIDs[i].len;}/* allocate space for encoded database record, including space * for low level header */dbitem->len=len+SEC_DB_ENTRY_HEADER_LEN;dbitem->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbitem->len);if(dbitem->data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}/* fill in database record */buf=&dbitem->data[SEC_DB_ENTRY_HEADER_LEN];buf[0]=(PRUint8)(ncerts>>8);buf[1]=(PRUint8)(ncerts);buf[2]=(PRUint8)(nnlen>>8);buf[3]=(PRUint8)(nnlen);/* v7 email field is NULL in v8 */buf[4]=0;buf[5]=0;PORT_Assert(DB_SUBJECT_ENTRY_HEADER_LEN==6);if(entry->nickname){PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN],entry->nickname,nnlen);}tmpbuf=&buf[keyidoff];for(i=0;i<ncerts;i++){tmpbuf[0]=(PRUint8)(certKeys[i].len>>8);tmpbuf[1]=(PRUint8)(certKeys[i].len);tmpbuf+=2;}for(i=0;i<ncerts;i++){tmpbuf[0]=(PRUint8)(keyIDs[i].len>>8);tmpbuf[1]=(PRUint8)(keyIDs[i].len);tmpbuf+=2;}for(i=0;i<ncerts;i++){PORT_Memcpy(tmpbuf,certKeys[i].data,certKeys[i].len);tmpbuf+=certKeys[i].len;}for(i=0;i<ncerts;i++){if(keyIDs[i].len){PORT_Memcpy(tmpbuf,keyIDs[i].data,keyIDs[i].len);tmpbuf+=keyIDs[i].len;}}if(entry->emailAddrs){tmpbuf[0]=(PRUint8)(entry->nemailAddrs>>8);tmpbuf[1]=(PRUint8)(entry->nemailAddrs);tmpbuf+=2;for(i=0;i<entry->nemailAddrs;i++){intnameLen=PORT_Strlen(entry->emailAddrs[i])+1;tmpbuf[0]=(PRUint8)(nameLen>>8);tmpbuf[1]=(PRUint8)(nameLen);tmpbuf+=2;PORT_Memcpy(tmpbuf,entry->emailAddrs[i],nameLen);tmpbuf+=nameLen;}}PORT_Assert(tmpbuf==&buf[len]);return(SECSuccess);loser:return(SECFailure);}/* * Encode a database key for a subject record */staticSECStatusEncodeDBSubjectKey(SECItem*derSubject,PLArenaPool*arena,SECItem*dbkey){dbkey->len=derSubject->len+SEC_DB_KEY_HEADER_LEN;if(dbkey->len>NSS_MAX_LEGACY_DB_KEY_SIZE)gotoloser;dbkey->data=(unsignedchar*)PORT_ArenaAlloc(arena,dbkey->len);if(dbkey->data==NULL){gotoloser;}PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],derSubject->data,derSubject->len);dbkey->data[0]=certDBEntryTypeSubject;return(SECSuccess);loser:return(SECFailure);}staticSECStatusDecodeDBSubjectEntry(certDBEntrySubject*entry,SECItem*dbentry,constSECItem*derSubject){PLArenaPool*arena=entry->common.arena;unsignedchar*tmpbuf;unsignedchar*end;void*mark=PORT_ArenaMark(arena);unsignedinteaddrlen;unsignedinti;unsignedintkeyidoff;unsignedintlen;unsignedintncerts=0;unsignedintnnlen;SECStatusrv;rv=SECITEM_CopyItem(arena,&entry->derSubject,derSubject);if(rv!=SECSuccess){gotoloser;}/* is record long enough for header? */if(dbentry->len<DB_SUBJECT_ENTRY_HEADER_LEN){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}entry->ncerts=ncerts=((dbentry->data[0]<<8)|dbentry->data[1]);nnlen=((dbentry->data[2]<<8)|dbentry->data[3]);eaddrlen=((dbentry->data[4]<<8)|dbentry->data[5]);keyidoff=DB_SUBJECT_ENTRY_HEADER_LEN+nnlen+eaddrlen;len=keyidoff+(4*ncerts);if(dbentry->len<len){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}entry->certKeys=PORT_ArenaNewArray(arena,SECItem,ncerts);entry->keyIDs=PORT_ArenaNewArray(arena,SECItem,ncerts);if((entry->certKeys==NULL)||(entry->keyIDs==NULL)){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}if(nnlen>1){/* null terminator is stored */entry->nickname=(char*)PORT_ArenaAlloc(arena,nnlen);if(entry->nickname==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->nickname,&dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],nnlen);}else{entry->nickname=NULL;}/* if we have an old style email entry, there is only one */entry->nemailAddrs=0;if(eaddrlen>1){/* null terminator is stored */entry->emailAddrs=PORT_ArenaNewArray(arena,char*,2);if(entry->emailAddrs==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry->emailAddrs[0]=(char*)PORT_ArenaAlloc(arena,eaddrlen);if(entry->emailAddrs[0]==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->emailAddrs[0],&dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN+nnlen],eaddrlen);entry->nemailAddrs=1;}else{entry->emailAddrs=NULL;}/* collect the lengths of the certKeys and keyIDs, and total the * overall length. */tmpbuf=&dbentry->data[keyidoff];for(i=0;i<ncerts;i++){unsignedintitemlen=(tmpbuf[0]<<8)|tmpbuf[1];entry->certKeys[i].len=itemlen;len+=itemlen;tmpbuf+=2;}for(i=0;i<ncerts;i++){unsignedintitemlen=(tmpbuf[0]<<8)|tmpbuf[1];entry->keyIDs[i].len=itemlen;len+=itemlen;tmpbuf+=2;}/* is encoded entry large enough ? */if(len>dbentry->len){PORT_SetError(SEC_ERROR_BAD_DATABASE);gotoloser;}for(i=0;i<ncerts;i++){unsignedintkLen=entry->certKeys[i].len;entry->certKeys[i].data=(unsignedchar*)PORT_ArenaAlloc(arena,kLen);if(entry->certKeys[i].data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->certKeys[i].data,tmpbuf,kLen);tmpbuf+=kLen;}for(i=0;i<ncerts;i++){unsignedintiLen=entry->keyIDs[i].len;entry->keyIDs[i].data=(unsignedchar*)PORT_ArenaAlloc(arena,iLen);if(entry->keyIDs[i].data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->keyIDs[i].data,tmpbuf,iLen);tmpbuf+=iLen;}end=dbentry->data+dbentry->len;if((eaddrlen==0)&&(end-tmpbuf>1)){/* read in the additional email addresses */entry->nemailAddrs=(((unsignedint)tmpbuf[0])<<8)|tmpbuf[1];tmpbuf+=2;if(end-tmpbuf<2*(int)entry->nemailAddrs)gotoloser;entry->emailAddrs=PORT_ArenaNewArray(arena,char*,entry->nemailAddrs);if(entry->emailAddrs==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}for(i=0;i<entry->nemailAddrs;i++){intnameLen;if(end-tmpbuf<2){gotoloser;}nameLen=(((int)tmpbuf[0])<<8)|tmpbuf[1];tmpbuf+=2;if(end-tmpbuf<nameLen){gotoloser;}entry->emailAddrs[i]=PORT_ArenaAlloc(arena,nameLen);if(entry->emailAddrs==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_Memcpy(entry->emailAddrs[i],tmpbuf,nameLen);tmpbuf+=nameLen;}if(tmpbuf!=end)gotoloser;}PORT_ArenaUnmark(arena,mark);return(SECSuccess);loser:PORT_ArenaRelease(arena,mark);/* discard above allocations */return(SECFailure);}/* * create a new subject entry with a single cert */staticcertDBEntrySubject*NewDBSubjectEntry(SECItem*derSubject,SECItem*certKey,SECItem*keyID,char*nickname,char*emailAddr,unsignedintflags){PLArenaPool*arena=NULL;certDBEntrySubject*entry;SECStatusrv;unsignedintnnlen;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry=(certDBEntrySubject*)PORT_ArenaAlloc(arena,sizeof(certDBEntrySubject));if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}/* init common fields */entry->common.arena=arena;entry->common.type=certDBEntryTypeSubject;entry->common.version=CERT_DB_FILE_VERSION;entry->common.flags=flags;/* copy the subject */rv=SECITEM_CopyItem(arena,&entry->derSubject,derSubject);if(rv!=SECSuccess){gotoloser;}entry->ncerts=1;entry->nemailAddrs=0;/* copy nickname */if(nickname&&(*nickname!='\0')){nnlen=PORT_Strlen(nickname)+1;entry->nickname=(char*)PORT_ArenaAlloc(arena,nnlen);if(entry->nickname==NULL){gotoloser;}PORT_Memcpy(entry->nickname,nickname,nnlen);}else{entry->nickname=NULL;}/* copy email addr */if(emailAddr&&(*emailAddr!='\0')){emailAddr=nsslowcert_FixupEmailAddr(emailAddr);if(emailAddr==NULL){entry->emailAddrs=NULL;gotoloser;}entry->emailAddrs=(char**)PORT_ArenaAlloc(arena,sizeof(char*));if(entry->emailAddrs==NULL){PORT_Free(emailAddr);gotoloser;}entry->emailAddrs[0]=PORT_ArenaStrdup(arena,emailAddr);if(entry->emailAddrs[0]){entry->nemailAddrs=1;}PORT_Free(emailAddr);}else{entry->emailAddrs=NULL;}/* allocate space for certKeys and keyIDs */entry->certKeys=(SECItem*)PORT_ArenaAlloc(arena,sizeof(SECItem));entry->keyIDs=(SECItem*)PORT_ArenaAlloc(arena,sizeof(SECItem));if((entry->certKeys==NULL)||(entry->keyIDs==NULL)){gotoloser;}/* copy the certKey and keyID */rv=SECITEM_CopyItem(arena,&entry->certKeys[0],certKey);if(rv!=SECSuccess){gotoloser;}rv=SECITEM_CopyItem(arena,&entry->keyIDs[0],keyID);if(rv!=SECSuccess){gotoloser;}return(entry);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(NULL);}/* * delete a subject entry */staticSECStatusDeleteDBSubjectEntry(NSSLOWCERTCertDBHandle*handle,SECItem*derSubject){SECItemdbkey;PLArenaPool*arena=NULL;SECStatusrv;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){gotoloser;}rv=EncodeDBSubjectKey(derSubject,arena,&dbkey);if(rv!=SECSuccess){gotoloser;}rv=DeleteDBEntry(handle,certDBEntryTypeSubject,&dbkey);if(rv==SECFailure){gotoloser;}PORT_FreeArena(arena,PR_FALSE);return(SECSuccess);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(SECFailure);}/* * Read the subject entry */staticcertDBEntrySubject*ReadDBSubjectEntry(NSSLOWCERTCertDBHandle*handle,SECItem*derSubject){/* |arena| isn't function-bounded, so cannot be a PORTCheapArenaPool. */PLArenaPool*arena=NULL;PORTCheapArenaPooltmpArena;certDBEntrySubject*entry;SECItemdbkey;SECItemdbentry;SECStatusrv;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}PORT_InitCheapArena(&tmpArena,DER_DEFAULT_CHUNKSIZE);entry=(certDBEntrySubject*)PORT_ArenaAlloc(arena,sizeof(certDBEntrySubject));if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry->common.arena=arena;entry->common.type=certDBEntryTypeSubject;rv=EncodeDBSubjectKey(derSubject,&tmpArena.arena,&dbkey);if(rv!=SECSuccess){gotoloser;}rv=ReadDBEntry(handle,&entry->common,&dbkey,&dbentry,&tmpArena.arena);if(rv==SECFailure){gotoloser;}rv=DecodeDBSubjectEntry(entry,&dbentry,derSubject);if(rv==SECFailure){gotoloser;}PORT_DestroyCheapArena(&tmpArena);return(entry);loser:PORT_DestroyCheapArena(&tmpArena);if(arena){PORT_FreeArena(arena,PR_FALSE);}return(NULL);}/* * Encode a subject name entry into byte stream suitable for * the database */staticSECStatusWriteDBSubjectEntry(NSSLOWCERTCertDBHandle*handle,certDBEntrySubject*entry){SECItemdbitem,dbkey;PLArenaPool*tmparena=NULL;SECStatusrv;tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){gotoloser;}rv=EncodeDBSubjectEntry(entry,tmparena,&dbitem);if(rv!=SECSuccess){gotoloser;}rv=EncodeDBSubjectKey(&entry->derSubject,tmparena,&dbkey);if(rv!=SECSuccess){gotoloser;}/* now write it to the database */rv=WriteDBEntry(handle,&entry->common,&dbkey,&dbitem);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(SECSuccess);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}return(SECFailure);}typedefenum{nsslowcert_remove,nsslowcert_add}nsslowcertUpdateType;staticSECStatusnsslowcert_UpdateSubjectEmailAddr(NSSLOWCERTCertDBHandle*dbhandle,SECItem*derSubject,char*emailAddr,nsslowcertUpdateTypeupdateType){certDBEntrySubject*entry=NULL;intindex=-1,i;SECStatusrv;if(emailAddr){emailAddr=nsslowcert_FixupEmailAddr(emailAddr);if(emailAddr==NULL){returnSECFailure;}}else{returnSECSuccess;}entry=ReadDBSubjectEntry(dbhandle,derSubject);if(entry==NULL){rv=SECFailure;gotodone;}for(i=0;i<(int)(entry->nemailAddrs);i++){if(PORT_Strcmp(entry->emailAddrs[i],emailAddr)==0){index=i;}}if(updateType==nsslowcert_remove){if(index==-1){rv=SECSuccess;gotodone;}entry->nemailAddrs--;for(i=index;i<(int)(entry->nemailAddrs);i++){entry->emailAddrs[i]=entry->emailAddrs[i+1];}}else{char**newAddrs=NULL;if(index!=-1){rv=SECSuccess;gotodone;}newAddrs=(char**)PORT_ArenaAlloc(entry->common.arena,(entry->nemailAddrs+1)*sizeof(char*));if(!newAddrs){rv=SECFailure;gotodone;}for(i=0;i<(int)(entry->nemailAddrs);i++){newAddrs[i]=entry->emailAddrs[i];}newAddrs[entry->nemailAddrs]=PORT_ArenaStrdup(entry->common.arena,emailAddr);if(!newAddrs[entry->nemailAddrs]){rv=SECFailure;gotodone;}entry->emailAddrs=newAddrs;entry->nemailAddrs++;}/* delete the subject entry */DeleteDBSubjectEntry(dbhandle,derSubject);/* write the new one */rv=WriteDBSubjectEntry(dbhandle,entry);done:if(entry)DestroyDBEntry((certDBEntry*)entry);if(emailAddr)PORT_Free(emailAddr);returnrv;}/* * writes a nickname to an existing subject entry that does not currently * have one */staticSECStatusAddNicknameToSubject(NSSLOWCERTCertDBHandle*dbhandle,NSSLOWCERTCertificate*cert,char*nickname){certDBEntrySubject*entry;SECStatusrv;if(nickname==NULL){return(SECFailure);}entry=ReadDBSubjectEntry(dbhandle,&cert->derSubject);PORT_Assert(entry!=NULL);if(entry==NULL){gotoloser;}PORT_Assert(entry->nickname==NULL);if(entry->nickname!=NULL){gotoloser;}entry->nickname=PORT_ArenaStrdup(entry->common.arena,nickname);if(entry->nickname==NULL){gotoloser;}/* delete the subject entry */DeleteDBSubjectEntry(dbhandle,&cert->derSubject);/* write the new one */rv=WriteDBSubjectEntry(dbhandle,entry);if(rv!=SECSuccess){gotoloser;}DestroyDBEntry((certDBEntry*)entry);return(SECSuccess);loser:DestroyDBEntry((certDBEntry*)entry);return(SECFailure);}/* * create a new version entry */staticcertDBEntryVersion*NewDBVersionEntry(unsignedintflags){PLArenaPool*arena=NULL;certDBEntryVersion*entry;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry=(certDBEntryVersion*)PORT_ArenaAlloc(arena,sizeof(certDBEntryVersion));if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry->common.arena=arena;entry->common.type=certDBEntryTypeVersion;entry->common.version=CERT_DB_FILE_VERSION;entry->common.flags=flags;return(entry);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(NULL);}/* * Read the version entry */staticcertDBEntryVersion*ReadDBVersionEntry(NSSLOWCERTCertDBHandle*handle){PLArenaPool*arena=NULL;PLArenaPool*tmparena=NULL;certDBEntryVersion*entry;SECItemdbkey;SECItemdbentry;SECStatusrv;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry=PORT_ArenaZNew(arena,certDBEntryVersion);if(entry==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}entry->common.arena=arena;entry->common.type=certDBEntryTypeVersion;/* now get the database key and format it */dbkey.len=SEC_DB_VERSION_KEY_LEN+SEC_DB_KEY_HEADER_LEN;dbkey.data=(unsignedchar*)PORT_ArenaAlloc(tmparena,dbkey.len);if(dbkey.data==NULL){gotoloser;}PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN],SEC_DB_VERSION_KEY,SEC_DB_VERSION_KEY_LEN);rv=ReadDBEntry(handle,&entry->common,&dbkey,&dbentry,tmparena);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(entry);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}if(arena){PORT_FreeArena(arena,PR_FALSE);}return(NULL);}/* * Encode a version entry into byte stream suitable for * the database */staticSECStatusWriteDBVersionEntry(NSSLOWCERTCertDBHandle*handle,certDBEntryVersion*entry){SECItemdbitem,dbkey;PLArenaPool*tmparena=NULL;SECStatusrv;tmparena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(tmparena==NULL){gotoloser;}/* allocate space for encoded database record, including space * for low level header */dbitem.len=SEC_DB_ENTRY_HEADER_LEN;dbitem.data=(unsignedchar*)PORT_ArenaAlloc(tmparena,dbitem.len);if(dbitem.data==NULL){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}/* now get the database key and format it */dbkey.len=SEC_DB_VERSION_KEY_LEN+SEC_DB_KEY_HEADER_LEN;dbkey.data=(unsignedchar*)PORT_ArenaAlloc(tmparena,dbkey.len);if(dbkey.data==NULL){gotoloser;}PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN],SEC_DB_VERSION_KEY,SEC_DB_VERSION_KEY_LEN);/* now write it to the database */rv=WriteDBEntry(handle,&entry->common,&dbkey,&dbitem);if(rv!=SECSuccess){gotoloser;}PORT_FreeArena(tmparena,PR_FALSE);return(SECSuccess);loser:if(tmparena){PORT_FreeArena(tmparena,PR_FALSE);}return(SECFailure);}/* * cert is no longer a perm cert, but will remain a temp cert */staticSECStatusRemovePermSubjectNode(NSSLOWCERTCertificate*cert){certDBEntrySubject*entry;unsignedinti;SECStatusrv;entry=ReadDBSubjectEntry(cert->dbhandle,&cert->derSubject);if(entry==NULL){return(SECFailure);}PORT_Assert(entry->ncerts);rv=SECFailure;if(entry->ncerts>1){for(i=0;i<entry->ncerts;i++){if(SECITEM_CompareItem(&entry->certKeys[i],&cert->certKey)==SECEqual){/* copy rest of list forward one entry */for(i=i+1;i<entry->ncerts;i++){entry->certKeys[i-1]=entry->certKeys[i];entry->keyIDs[i-1]=entry->keyIDs[i];}entry->ncerts--;DeleteDBSubjectEntry(cert->dbhandle,&cert->derSubject);rv=WriteDBSubjectEntry(cert->dbhandle,entry);break;}}}else{/* no entries left, delete the perm entry in the DB */if(entry->emailAddrs){/* if the subject had an email record, then delete it too */for(i=0;i<entry->nemailAddrs;i++){DeleteDBSMimeEntry(cert->dbhandle,entry->emailAddrs[i]);}}if(entry->nickname){DeleteDBNicknameEntry(cert->dbhandle,entry->nickname);}DeleteDBSubjectEntry(cert->dbhandle,&cert->derSubject);}DestroyDBEntry((certDBEntry*)entry);return(rv);}/* * add a cert to the perm subject list */staticSECStatusAddPermSubjectNode(certDBEntrySubject*entry,NSSLOWCERTCertificate*cert,char*nickname){SECItem*newCertKeys,*newKeyIDs;unsignedinti,new_i;SECStatusrv;unsignedintncerts;PORT_Assert(entry);ncerts=entry->ncerts;if(nickname&&entry->nickname){/* nicknames must be the same */PORT_Assert(PORT_Strcmp(nickname,entry->nickname)==0);}if((entry->nickname==NULL)&&(nickname!=NULL)){/* copy nickname into the entry */entry->nickname=PORT_ArenaStrdup(entry->common.arena,nickname);if(entry->nickname==NULL){return(SECFailure);}}/* a DB entry already exists, so add this cert */newCertKeys=PORT_ArenaZNewArray(entry->common.arena,SECItem,ncerts+1);newKeyIDs=PORT_ArenaZNewArray(entry->common.arena,SECItem,ncerts+1);if((newCertKeys==NULL)||(newKeyIDs==NULL)){return(SECFailure);}/* Step 1: copy certs older than "cert" into new entry. */for(i=0,new_i=0;i<ncerts;i++){NSSLOWCERTCertificate*cmpcert;PRBoolisNewer;cmpcert=nsslowcert_FindCertByKey(cert->dbhandle,&entry->certKeys[i]);/* The entry has been corrupted, remove it from the list */if(!cmpcert){continue;}isNewer=nsslowcert_IsNewer(cert,cmpcert);nsslowcert_DestroyCertificate(cmpcert);if(isNewer)break;/* copy this cert entry */newCertKeys[new_i]=entry->certKeys[i];newKeyIDs[new_i]=entry->keyIDs[i];new_i++;}/* Step 2: Add "cert" to the entry. */rv=SECITEM_CopyItem(entry->common.arena,&newCertKeys[new_i],&cert->certKey);if(rv!=SECSuccess){return(SECFailure);}rv=SECITEM_CopyItem(entry->common.arena,&newKeyIDs[new_i],&cert->subjectKeyID);if(rv!=SECSuccess){return(SECFailure);}new_i++;/* Step 3: copy remaining certs (if any) from old entry to new. */for(;i<ncerts;i++,new_i++){newCertKeys[new_i]=entry->certKeys[i];newKeyIDs[new_i]=entry->keyIDs[i];}/* update certKeys and keyIDs */entry->certKeys=newCertKeys;entry->keyIDs=newKeyIDs;/* set new count value */entry->ncerts=new_i;DeleteDBSubjectEntry(cert->dbhandle,&cert->derSubject);rv=WriteDBSubjectEntry(cert->dbhandle,entry);return(rv);}SECStatusnsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle*handle,SECItem*derSubject,NSSLOWCERTCertCallbackcb,void*cbarg){certDBEntrySubject*entry;unsignedinti;NSSLOWCERTCertificate*cert;SECStatusrv=SECSuccess;entry=ReadDBSubjectEntry(handle,derSubject);if(entry==NULL){return(SECFailure);}for(i=0;i<entry->ncerts;i++){cert=nsslowcert_FindCertByKey(handle,&entry->certKeys[i]);if(!cert){continue;}rv=(*cb)(cert,cbarg);nsslowcert_DestroyCertificate(cert);if(rv==SECFailure){break;}}DestroyDBEntry((certDBEntry*)entry);return(rv);}intnsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle*handle,SECItem*derSubject){certDBEntrySubject*entry;intret;entry=ReadDBSubjectEntry(handle,derSubject);if(entry==NULL){return(SECFailure);}ret=entry->ncerts;DestroyDBEntry((certDBEntry*)entry);return(ret);}SECStatusnsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle*handle,char*nickname,NSSLOWCERTCertCallbackcb,void*cbarg){certDBEntryNickname*nnentry=NULL;certDBEntrySMime*smentry=NULL;SECStatusrv;SECItem*derSubject=NULL;nnentry=ReadDBNicknameEntry(handle,nickname);if(nnentry){derSubject=&nnentry->subjectName;}else{smentry=nsslowcert_ReadDBSMimeEntry(handle,nickname);if(smentry){derSubject=&smentry->subjectName;}}if(derSubject){rv=nsslowcert_TraversePermCertsForSubject(handle,derSubject,cb,cbarg);}else{rv=SECFailure;}if(nnentry){DestroyDBEntry((certDBEntry*)nnentry);}if(smentry){DestroyDBEntry((certDBEntry*)smentry);}return(rv);}intnsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle*handle,char*nickname){certDBEntryNickname*entry;intret;entry=ReadDBNicknameEntry(handle,nickname);if(entry){ret=nsslowcert_NumPermCertsForSubject(handle,&entry->subjectName);DestroyDBEntry((certDBEntry*)entry);}else{ret=0;}return(ret);}/* * add a nickname to a cert that doesn't have one */staticSECStatusAddNicknameToPermCert(NSSLOWCERTCertDBHandle*dbhandle,NSSLOWCERTCertificate*cert,char*nickname){certDBEntryCert*entry;intrv;entry=cert->dbEntry;PORT_Assert(entry!=NULL);if(entry==NULL){gotoloser;}pkcs11_freeNickname(entry->nickname,entry->nicknameSpace);entry->nickname=NULL;entry->nickname=pkcs11_copyNickname(nickname,entry->nicknameSpace,sizeof(entry->nicknameSpace));rv=WriteDBCertEntry(dbhandle,entry);if(rv){gotoloser;}pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);cert->nickname=NULL;cert->nickname=pkcs11_copyNickname(nickname,cert->nicknameSpace,sizeof(cert->nicknameSpace));return(SECSuccess);loser:return(SECFailure);}/* * add a nickname to a cert that is already in the perm database, but doesn't * have one yet (it is probably an e-mail cert). */SECStatusnsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle*dbhandle,NSSLOWCERTCertificate*cert,char*nickname){SECStatusrv=SECFailure;certDBEntrySubject*entry=NULL;certDBEntryNickname*nicknameEntry=NULL;nsslowcert_LockDB(dbhandle);entry=ReadDBSubjectEntry(dbhandle,&cert->derSubject);if(entry==NULL)gotoloser;if(entry->nickname==NULL){/* no nickname for subject */rv=AddNicknameToSubject(dbhandle,cert,nickname);if(rv!=SECSuccess){gotoloser;}rv=AddNicknameToPermCert(dbhandle,cert,nickname);if(rv!=SECSuccess){gotoloser;}nicknameEntry=NewDBNicknameEntry(nickname,&cert->derSubject,0);if(nicknameEntry==NULL){gotoloser;}rv=WriteDBNicknameEntry(dbhandle,nicknameEntry);if(rv!=SECSuccess){gotoloser;}}else{/* subject already has a nickname */rv=AddNicknameToPermCert(dbhandle,cert,entry->nickname);if(rv!=SECSuccess){gotoloser;}/* make sure nickname entry exists. If the database was corrupted, * we may have lost the nickname entry. Add it back now */nicknameEntry=ReadDBNicknameEntry(dbhandle,entry->nickname);if(nicknameEntry==NULL){nicknameEntry=NewDBNicknameEntry(entry->nickname,&cert->derSubject,0);if(nicknameEntry==NULL){gotoloser;}rv=WriteDBNicknameEntry(dbhandle,nicknameEntry);if(rv!=SECSuccess){gotoloser;}}}rv=SECSuccess;loser:if(entry){DestroyDBEntry((certDBEntry*)entry);}if(nicknameEntry){DestroyDBEntry((certDBEntry*)nicknameEntry);}nsslowcert_UnlockDB(dbhandle);return(rv);}staticcertDBEntryCert*AddCertToPermDB(NSSLOWCERTCertDBHandle*handle,NSSLOWCERTCertificate*cert,char*nickname,NSSLOWCERTCertTrust*trust){certDBEntryCert*certEntry=NULL;certDBEntryNickname*nicknameEntry=NULL;certDBEntrySubject*subjectEntry=NULL;intstate=0;SECStatusrv;PRBooldonnentry=PR_FALSE;if(nickname){donnentry=PR_TRUE;}subjectEntry=ReadDBSubjectEntry(handle,&cert->derSubject);if(subjectEntry&&subjectEntry->nickname){donnentry=PR_FALSE;nickname=subjectEntry->nickname;}certEntry=NewDBCertEntry(&cert->derCert,nickname,trust,0);if(certEntry==NULL){gotoloser;}if(donnentry){nicknameEntry=NewDBNicknameEntry(nickname,&cert->derSubject,0);if(nicknameEntry==NULL){gotoloser;}}rv=WriteDBCertEntry(handle,certEntry);if(rv!=SECSuccess){gotoloser;}state=1;if(nicknameEntry){rv=WriteDBNicknameEntry(handle,nicknameEntry);if(rv!=SECSuccess){gotoloser;}}state=2;/* "Change" handles if necessary */cert->dbhandle=handle;/* add to or create new subject entry */if(subjectEntry){/* REWRITE BASED ON SUBJECT ENTRY */rv=AddPermSubjectNode(subjectEntry,cert,nickname);if(rv!=SECSuccess){gotoloser;}}else{/* make a new subject entry - this case is only used when updating * an old version of the database. This is OK because the oldnickname * db format didn't allow multiple certs with the same subject. *//* where does subjectKeyID and certKey come from? */subjectEntry=NewDBSubjectEntry(&cert->derSubject,&cert->certKey,&cert->subjectKeyID,nickname,NULL,0);if(subjectEntry==NULL){gotoloser;}rv=WriteDBSubjectEntry(handle,subjectEntry);if(rv!=SECSuccess){gotoloser;}}state=3;if(nicknameEntry){DestroyDBEntry((certDBEntry*)nicknameEntry);}if(subjectEntry){DestroyDBEntry((certDBEntry*)subjectEntry);}return(certEntry);loser:/* don't leave partial entry in the database */if(state>0){DeleteDBCertEntry(handle,&cert->certKey);}if((state>1)&&donnentry){DeleteDBNicknameEntry(handle,nickname);}if(certEntry){DestroyDBEntry((certDBEntry*)certEntry);}if(nicknameEntry){DestroyDBEntry((certDBEntry*)nicknameEntry);}if(subjectEntry){DestroyDBEntry((certDBEntry*)subjectEntry);}return(NULL);}/* forward declaration */staticSECStatusUpdateV7DB(NSSLOWCERTCertDBHandle*handle,DB*updatedb);/* * version 8 uses the same schema as version 7. The only differences are * 1) version 8 db uses the blob shim to store data entries > 32k. * 2) version 8 db sets the db block size to 32k. * both of these are dealt with by the handle. */staticSECStatusUpdateV8DB(NSSLOWCERTCertDBHandle*handle,DB*updatedb){returnUpdateV7DB(handle,updatedb);}/* * we could just blindly sequence through reading key data pairs and writing * them back out, but some cert.db's have gotten quite large and may have some * subtle corruption problems, so instead we cycle through the certs and * CRL's and S/MIME profiles and rebuild our subject lists from those records. */staticSECStatusUpdateV7DB(NSSLOWCERTCertDBHandle*handle,DB*updatedb){DBTkey,data;intret;NSSLOWCERTCertificate*cert;PRBoolisKRL=PR_FALSE;certDBEntryTypeentryType;SECItemdbEntry,dbKey;certDBEntryRevocationcrlEntry;certDBEntryCertcertEntry;certDBEntrySMimesmimeEntry;SECStatusrv;ret=(*updatedb->seq)(updatedb,&key,&data,R_FIRST);if(ret){return(SECFailure);}do{unsignedchar*dataBuf=(unsignedchar*)data.data;unsignedchar*keyBuf=(unsignedchar*)key.data;dbEntry.data=&dataBuf[SEC_DB_ENTRY_HEADER_LEN];dbEntry.len=data.size-SEC_DB_ENTRY_HEADER_LEN;entryType=(certDBEntryType)keyBuf[0];dbKey.data=&keyBuf[SEC_DB_KEY_HEADER_LEN];dbKey.len=key.size-SEC_DB_KEY_HEADER_LEN;if((dbEntry.len<=0)||(dbKey.len<=0)){continue;}switch(entryType){/* these entries will get regenerated as we read the * rest of the data from the database */casecertDBEntryTypeVersion:casecertDBEntryTypeSubject:casecertDBEntryTypeContentVersion:casecertDBEntryTypeNickname:/* smime profiles need entries created after the certs have * been imported, loop over them in a second run */casecertDBEntryTypeSMimeProfile:break;casecertDBEntryTypeCert:/* decode Entry */certEntry.common.version=(unsignedint)dataBuf[0];certEntry.common.type=entryType;certEntry.common.flags=(unsignedint)dataBuf[2];rv=DecodeDBCertEntry(&certEntry,&dbEntry);if(rv!=SECSuccess){break;}/* should we check for existing duplicates? */cert=nsslowcert_DecodeDERCertificate(&certEntry.derCert,certEntry.nickname);if(cert){nsslowcert_UpdatePermCert(handle,cert,certEntry.nickname,&certEntry.trust);nsslowcert_DestroyCertificate(cert);}/* free any data the decode may have allocated. */pkcs11_freeStaticData(certEntry.derCert.data,certEntry.derCertSpace);pkcs11_freeNickname(certEntry.nickname,certEntry.nicknameSpace);break;casecertDBEntryTypeKeyRevocation:isKRL=PR_TRUE;/* fall through */casecertDBEntryTypeRevocation:crlEntry.common.version=(unsignedint)dataBuf[0];crlEntry.common.type=entryType;crlEntry.common.flags=(unsignedint)dataBuf[2];crlEntry.common.arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(crlEntry.common.arena==NULL){break;}rv=DecodeDBCrlEntry(&crlEntry,&dbEntry);if(rv!=SECSuccess){break;}nsslowcert_UpdateCrl(handle,&crlEntry.derCrl,&dbKey,crlEntry.url,isKRL);/* free data allocated by the decode */PORT_FreeArena(crlEntry.common.arena,PR_FALSE);crlEntry.common.arena=NULL;break;default:break;}}while((*updatedb->seq)(updatedb,&key,&data,R_NEXT)==0);/* now loop again updating just the SMimeProfile. */ret=(*updatedb->seq)(updatedb,&key,&data,R_FIRST);if(ret){return(SECFailure);}do{unsignedchar*dataBuf=(unsignedchar*)data.data;unsignedchar*keyBuf=(unsignedchar*)key.data;dbEntry.data=&dataBuf[SEC_DB_ENTRY_HEADER_LEN];dbEntry.len=data.size-SEC_DB_ENTRY_HEADER_LEN;entryType=(certDBEntryType)keyBuf[0];if(entryType!=certDBEntryTypeSMimeProfile){continue;}dbKey.data=&keyBuf[SEC_DB_KEY_HEADER_LEN];dbKey.len=key.size-SEC_DB_KEY_HEADER_LEN;if((dbEntry.len<=0)||(dbKey.len<=0)){continue;}smimeEntry.common.version=(unsignedint)dataBuf[0];smimeEntry.common.type=entryType;smimeEntry.common.flags=(unsignedint)dataBuf[2];smimeEntry.common.arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);/* decode entry */rv=DecodeDBSMimeEntry(&smimeEntry,&dbEntry,(char*)dbKey.data);if(rv==SECSuccess){nsslowcert_UpdateSMimeProfile(handle,smimeEntry.emailAddr,&smimeEntry.subjectName,&smimeEntry.smimeOptions,&smimeEntry.optionsDate);}PORT_FreeArena(smimeEntry.common.arena,PR_FALSE);smimeEntry.common.arena=NULL;}while((*updatedb->seq)(updatedb,&key,&data,R_NEXT)==0);(*updatedb->close)(updatedb);/* a database update is a good time to go back and verify the integrity of * the keys and certs */handle->dbVerify=PR_TRUE;return(SECSuccess);}/* * NOTE - Version 6 DB did not go out to the real world in a release, * so we can remove this function in a later release. */staticSECStatusUpdateV6DB(NSSLOWCERTCertDBHandle*handle,DB*updatedb){intret;DBTkey,data;unsignedchar*buf,*tmpbuf=NULL;certDBEntryTypetype;certDBEntryNickname*nnEntry=NULL;certDBEntrySubject*subjectEntry=NULL;certDBEntrySMime*emailEntry=NULL;char*nickname;char*emailAddr;/* * Sequence through the old database and copy all of the entries * to the new database. Subject name entries will have the new * fields inserted into them (with zero length). */ret=(*updatedb->seq)(updatedb,&key,&data,R_FIRST);if(ret){return(SECFailure);}do{buf=(unsignedchar*)data.data;if(data.size>=3){if(buf[0]==6){/* version number */type=(certDBEntryType)buf[1];if(type==certDBEntryTypeSubject){/* expando subjecto entrieo */tmpbuf=(unsignedchar*)PORT_Alloc(data.size+4);if(tmpbuf){/* copy header stuff */PORT_Memcpy(tmpbuf,buf,SEC_DB_ENTRY_HEADER_LEN+2);/* insert 4 more bytes of zero'd header */PORT_Memset(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN+2],0,4);/* copy rest of the data */PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN+6],&buf[SEC_DB_ENTRY_HEADER_LEN+2],data.size-(SEC_DB_ENTRY_HEADER_LEN+2));data.data=(void*)tmpbuf;data.size+=4;buf=tmpbuf;}}elseif(type==certDBEntryTypeCert){/* expando certo entrieo */tmpbuf=(unsignedchar*)PORT_Alloc(data.size+3);if(tmpbuf){/* copy header stuff */PORT_Memcpy(tmpbuf,buf,SEC_DB_ENTRY_HEADER_LEN);/* copy trust flage, setting msb's to 0 */tmpbuf[SEC_DB_ENTRY_HEADER_LEN]=0;tmpbuf[SEC_DB_ENTRY_HEADER_LEN+1]=buf[SEC_DB_ENTRY_HEADER_LEN];tmpbuf[SEC_DB_ENTRY_HEADER_LEN+2]=0;tmpbuf[SEC_DB_ENTRY_HEADER_LEN+3]=buf[SEC_DB_ENTRY_HEADER_LEN+1];tmpbuf[SEC_DB_ENTRY_HEADER_LEN+4]=0;tmpbuf[SEC_DB_ENTRY_HEADER_LEN+5]=buf[SEC_DB_ENTRY_HEADER_LEN+2];/* copy rest of the data */PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN+6],&buf[SEC_DB_ENTRY_HEADER_LEN+3],data.size-(SEC_DB_ENTRY_HEADER_LEN+3));data.data=(void*)tmpbuf;data.size+=3;buf=tmpbuf;}}/* update the record version number */buf[0]=CERT_DB_FILE_VERSION;/* copy to the new database */ret=certdb_Put(handle->permCertDB,&key,&data,0);if(tmpbuf){PORT_Free(tmpbuf);tmpbuf=NULL;}if(ret){returnSECFailure;}}}}while((*updatedb->seq)(updatedb,&key,&data,R_NEXT)==0);ret=certdb_Sync(handle->permCertDB,0);if(ret){returnSECFailure;}ret=(*updatedb->seq)(updatedb,&key,&data,R_FIRST);if(ret){return(SECFailure);}do{buf=(unsignedchar*)data.data;if(data.size>=3){if(buf[0]==CERT_DB_FILE_VERSION){/* version number */type=(certDBEntryType)buf[1];if(type==certDBEntryTypeNickname){nickname=&((char*)key.data)[1];/* get the matching nickname entry in the new DB */nnEntry=ReadDBNicknameEntry(handle,nickname);if(nnEntry==NULL){gotoendloop;}/* find the subject entry pointed to by nickname */subjectEntry=ReadDBSubjectEntry(handle,&nnEntry->subjectName);if(subjectEntry==NULL){gotoendloop;}subjectEntry->nickname=(char*)PORT_ArenaAlloc(subjectEntry->common.arena,key.size-1);if(subjectEntry->nickname){PORT_Memcpy(subjectEntry->nickname,nickname,key.size-1);(void)WriteDBSubjectEntry(handle,subjectEntry);}}elseif(type==certDBEntryTypeSMimeProfile){emailAddr=&((char*)key.data)[1];/* get the matching smime entry in the new DB */emailEntry=nsslowcert_ReadDBSMimeEntry(handle,emailAddr);if(emailEntry==NULL){gotoendloop;}/* find the subject entry pointed to by nickname */subjectEntry=ReadDBSubjectEntry(handle,&emailEntry->subjectName);if(subjectEntry==NULL){gotoendloop;}subjectEntry->emailAddrs=(char**)PORT_ArenaAlloc(subjectEntry->common.arena,sizeof(char*));if(subjectEntry->emailAddrs){subjectEntry->emailAddrs[0]=(char*)PORT_ArenaAlloc(subjectEntry->common.arena,key.size-1);if(subjectEntry->emailAddrs[0]){PORT_Memcpy(subjectEntry->emailAddrs[0],emailAddr,key.size-1);subjectEntry->nemailAddrs=1;(void)WriteDBSubjectEntry(handle,subjectEntry);}}}endloop:if(subjectEntry){DestroyDBEntry((certDBEntry*)subjectEntry);subjectEntry=NULL;}if(nnEntry){DestroyDBEntry((certDBEntry*)nnEntry);nnEntry=NULL;}if(emailEntry){DestroyDBEntry((certDBEntry*)emailEntry);emailEntry=NULL;}}}}while((*updatedb->seq)(updatedb,&key,&data,R_NEXT)==0);ret=certdb_Sync(handle->permCertDB,0);if(ret){returnSECFailure;}(*updatedb->close)(updatedb);return(SECSuccess);}staticSECStatusupdateV5Callback(NSSLOWCERTCertificate*cert,SECItem*k,void*pdata){NSSLOWCERTCertDBHandle*handle;certDBEntryCert*entry;NSSLOWCERTCertTrust*trust;handle=(NSSLOWCERTCertDBHandle*)pdata;trust=&cert->dbEntry->trust;/* SSL user certs can be used for email if they have an email addr */if(cert->emailAddr&&(trust->sslFlags&CERTDB_USER)&&(trust->emailFlags==0)){trust->emailFlags=CERTDB_USER;}/* servers didn't set the user flags on the server cert.. */if(PORT_Strcmp(cert->dbEntry->nickname,"Server-Cert")==0){trust->sslFlags|=CERTDB_USER;}entry=AddCertToPermDB(handle,cert,cert->dbEntry->nickname,&cert->dbEntry->trust);if(entry){DestroyDBEntry((certDBEntry*)entry);}return(SECSuccess);}staticSECStatusUpdateV5DB(NSSLOWCERTCertDBHandle*handle,DB*updatedb){NSSLOWCERTCertDBHandleupdatehandle;updatehandle.permCertDB=updatedb;updatehandle.dbMon=PZ_NewMonitor(nssILockCertDB);updatehandle.dbVerify=0;updatehandle.ref=1;/* prevent premature close */(void)nsslowcert_TraversePermCerts(&updatehandle,updateV5Callback,(void*)handle);PZ_DestroyMonitor(updatehandle.dbMon);(*updatedb->close)(updatedb);return(SECSuccess);}staticPRBoolisV4DB(DB*db){DBTkey,data;intret;key.data="Version";key.size=7;ret=(*db->get)(db,&key,&data,0);if(ret){returnPR_FALSE;}if((data.size==1)&&(*(unsignedchar*)data.data<=4)){returnPR_TRUE;}returnPR_FALSE;}staticSECStatusUpdateV4DB(NSSLOWCERTCertDBHandle*handle,DB*updatedb){DBTkey,data;certDBEntryCert*entry,*entry2;intret;NSSLOWCERTCertificate*cert;ret=(*updatedb->seq)(updatedb,&key,&data,R_FIRST);if(ret){return(SECFailure);}do{if(data.size!=1){/* skip version number *//* decode the old DB entry */entry=(certDBEntryCert*)DecodeV4DBCertEntry((unsignedchar*)data.data,data.size);if(entry){cert=nsslowcert_DecodeDERCertificate(&entry->derCert,entry->nickname);if(cert!=NULL){/* add to new database */entry2=AddCertToPermDB(handle,cert,entry->nickname,&entry->trust);nsslowcert_DestroyCertificate(cert);if(entry2){DestroyDBEntry((certDBEntry*)entry2);}}DestroyDBEntry((certDBEntry*)entry);}}}while((*updatedb->seq)(updatedb,&key,&data,R_NEXT)==0);(*updatedb->close)(updatedb);return(SECSuccess);}/* * return true if a database key conflict exists */PRBoolnsslowcert_CertDBKeyConflict(SECItem*derCert,NSSLOWCERTCertDBHandle*handle){SECStatusrv;DBTtmpdata;DBTnamekey;intret;SECItemkeyitem;PLArenaPool*arena=NULL;SECItemderKey;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){gotoloser;}/* get the db key of the cert */rv=nsslowcert_KeyFromDERCert(arena,derCert,&derKey);if(rv!=SECSuccess){gotoloser;}rv=EncodeDBCertKey(&derKey,arena,&keyitem);if(rv!=SECSuccess){gotoloser;}namekey.data=keyitem.data;namekey.size=keyitem.len;ret=certdb_Get(handle->permCertDB,&namekey,&tmpdata,0);if(ret==0){gotoloser;}PORT_FreeArena(arena,PR_FALSE);return(PR_FALSE);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(PR_TRUE);}/* * return true if a nickname conflict exists * NOTE: caller must have already made sure that this exact cert * doesn't exist in the DB */staticPRBoolnsslowcert_CertNicknameConflict(char*nickname,SECItem*derSubject,NSSLOWCERTCertDBHandle*handle){PRBoolrv;certDBEntryNickname*entry;if(nickname==NULL){return(PR_FALSE);}entry=ReadDBNicknameEntry(handle,nickname);if(entry==NULL){/* no entry for this nickname, so no conflict */return(PR_FALSE);}rv=PR_TRUE;if(SECITEM_CompareItem(derSubject,&entry->subjectName)==SECEqual){/* if subject names are the same, then no conflict */rv=PR_FALSE;}DestroyDBEntry((certDBEntry*)entry);return(rv);}#ifdef DBM_USING_NSPR#define NO_RDONLY PR_RDONLY#define NO_RDWR PR_RDWR#define NO_CREATE (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)#else#define NO_RDONLY O_RDONLY#define NO_RDWR O_RDWR#define NO_CREATE (O_RDWR | O_CREAT | O_TRUNC)#endif/* * open an old database that needs to be updated */staticDB*nsslowcert_openolddb(NSSLOWCERTDBNameFuncnamecb,void*cbarg,intversion){char*tmpname;DB*updatedb=NULL;tmpname=(*namecb)(cbarg,version);/* get v6 db name */if(tmpname){updatedb=dbopen(tmpname,NO_RDONLY,0600,DB_HASH,0);PORT_Free(tmpname);}returnupdatedb;}staticSECStatusopenNewCertDB(constchar*appName,constchar*prefix,constchar*certdbname,NSSLOWCERTCertDBHandle*handle,NSSLOWCERTDBNameFuncnamecb,void*cbarg){SECStatusrv;certDBEntryVersion*versionEntry=NULL;DB*updatedb=NULL;intstatus=RDB_FAIL;if(appName){handle->permCertDB=rdbopen(appName,prefix,"cert",NO_CREATE,&status);}else{handle->permCertDB=dbsopen(certdbname,NO_CREATE,0600,DB_HASH,0);}/* if create fails then we lose */if(handle->permCertDB==0){returnstatus==RDB_RETRY?SECWouldBlock:SECFailure;}/* Verify version number; */versionEntry=NewDBVersionEntry(0);if(versionEntry==NULL){rv=SECFailure;gotoloser;}rv=WriteDBVersionEntry(handle,versionEntry);DestroyDBEntry((certDBEntry*)versionEntry);if(rv!=SECSuccess){gotoloser;}/* rv must already be Success here because of previous if statement *//* try to upgrade old db here */if(appName&&(updatedb=dbsopen(certdbname,NO_RDONLY,0600,DB_HASH,0))!=NULL){rv=UpdateV8DB(handle,updatedb);}elseif((updatedb=nsslowcert_openolddb(namecb,cbarg,7))!=NULL){rv=UpdateV7DB(handle,updatedb);}elseif((updatedb=nsslowcert_openolddb(namecb,cbarg,6))!=NULL){rv=UpdateV6DB(handle,updatedb);}elseif((updatedb=nsslowcert_openolddb(namecb,cbarg,5))!=NULL){rv=UpdateV5DB(handle,updatedb);}elseif((updatedb=nsslowcert_openolddb(namecb,cbarg,4))!=NULL){/* NES has v5 format db's with v4 db names! */if(isV4DB(updatedb)){rv=UpdateV4DB(handle,updatedb);}else{rv=UpdateV5DB(handle,updatedb);}}loser:db_InitComplete(handle->permCertDB);returnrv;}staticintnsslowcert_GetVersionNumber(NSSLOWCERTCertDBHandle*handle){certDBEntryVersion*versionEntry=NULL;intversion=0;versionEntry=ReadDBVersionEntry(handle);if(versionEntry==NULL){return0;}version=versionEntry->common.version;DestroyDBEntry((certDBEntry*)versionEntry);returnversion;}/* * Open the certificate database and index databases. Create them if * they are not there or bad. */staticSECStatusnsslowcert_OpenPermCertDB(NSSLOWCERTCertDBHandle*handle,PRBoolreadOnly,constchar*appName,constchar*prefix,NSSLOWCERTDBNameFuncnamecb,void*cbarg){SECStatusrv;intopenflags;char*certdbname;intversion=0;certdbname=(*namecb)(cbarg,CERT_DB_FILE_VERSION);if(certdbname==NULL){return(SECFailure);}openflags=readOnly?NO_RDONLY:NO_RDWR;/* * first open the permanent file based database. */if(appName){handle->permCertDB=rdbopen(appName,prefix,"cert",openflags,NULL);}else{handle->permCertDB=dbsopen(certdbname,openflags,0600,DB_HASH,0);}/* check for correct version number */if(handle->permCertDB){version=nsslowcert_GetVersionNumber(handle);if((version!=CERT_DB_FILE_VERSION)&&!(appName&&version==CERT_DB_V7_FILE_VERSION)){gotoloser;}}elseif(readOnly){/* don't create if readonly *//* Try openning a version 7 database */handle->permCertDB=nsslowcert_openolddb(namecb,cbarg,7);if(!handle->permCertDB){gotoloser;}if(nsslowcert_GetVersionNumber(handle)!=7){gotoloser;}}else{/* if first open fails, try to create a new DB */rv=openNewCertDB(appName,prefix,certdbname,handle,namecb,cbarg);if(rv==SECWouldBlock){/* only the rdb version can fail with wouldblock */handle->permCertDB=rdbopen(appName,prefix,"cert",openflags,NULL);/* check for correct version number */if(!handle->permCertDB){gotoloser;}version=nsslowcert_GetVersionNumber(handle);if((version!=CERT_DB_FILE_VERSION)&&!(appName&&version==CERT_DB_V7_FILE_VERSION)){gotoloser;}}elseif(rv!=SECSuccess){gotoloser;}}PORT_Free(certdbname);return(SECSuccess);loser:PORT_SetError(SEC_ERROR_BAD_DATABASE);if(handle->permCertDB){certdb_Close(handle->permCertDB);handle->permCertDB=0;}PORT_Free(certdbname);return(SECFailure);}/* * delete all DB records associated with a particular certificate */staticSECStatusDeletePermCert(NSSLOWCERTCertificate*cert){SECStatusrv;SECStatusret;ret=SECSuccess;rv=DeleteDBCertEntry(cert->dbhandle,&cert->certKey);if(rv!=SECSuccess){ret=SECFailure;}rv=RemovePermSubjectNode(cert);return(ret);}/* * Delete a certificate from the permanent database. */SECStatusnsslowcert_DeletePermCertificate(NSSLOWCERTCertificate*cert){SECStatusrv;nsslowcert_LockDB(cert->dbhandle);/* delete the records from the permanent database */rv=DeletePermCert(cert);/* get rid of dbcert and stuff pointing to it */DestroyDBEntry((certDBEntry*)cert->dbEntry);cert->dbEntry=NULL;cert->trust=NULL;nsslowcert_UnlockDB(cert->dbhandle);return(rv);}/* * Traverse all of the entries in the database of a particular type * call the given function for each one. */SECStatusnsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle*handle,certDBEntryTypetype,SECStatus(*callback)(SECItem*data,SECItem*key,certDBEntryTypetype,void*pdata),void*udata){DBTdata;DBTkey;SECStatusrv=SECSuccess;intret;SECItemdataitem;SECItemkeyitem;unsignedchar*buf;unsignedchar*keybuf;ret=certdb_Seq(handle->permCertDB,&key,&data,R_FIRST);if(ret){return(SECFailure);}/* here, ret is zero and rv is SECSuccess. * Below here, ret is a count of successful calls to the callback function. */do{buf=(unsignedchar*)data.data;if(buf[1]==(unsignedchar)type){dataitem.len=data.size;dataitem.data=buf;dataitem.type=siBuffer;keyitem.len=key.size-SEC_DB_KEY_HEADER_LEN;keybuf=(unsignedchar*)key.data;keyitem.data=&keybuf[SEC_DB_KEY_HEADER_LEN];keyitem.type=siBuffer;/* type should equal keybuf[0]. */rv=(*callback)(&dataitem,&keyitem,type,udata);if(rv==SECSuccess){++ret;}}}while(certdb_Seq(handle->permCertDB,&key,&data,R_NEXT)==0);/* If any callbacks succeeded, or no calls to callbacks were made, * then report success. Otherwise, report failure. */return(ret?SECSuccess:rv);}/* * Decode a certificate and enter it into the temporary certificate database. * Deal with nicknames correctly * * This is the private entry point. */staticNSSLOWCERTCertificate*DecodeACert(NSSLOWCERTCertDBHandle*handle,certDBEntryCert*entry){NSSLOWCERTCertificate*cert=NULL;cert=nsslowcert_DecodeDERCertificate(&entry->derCert,entry->nickname);if(cert==NULL){gotoloser;}cert->dbhandle=handle;cert->dbEntry=entry;cert->trust=&entry->trust;return(cert);loser:return(0);}staticNSSLOWCERTTrust*CreateTrust(void){NSSLOWCERTTrust*trust=NULL;nsslowcert_LockFreeList();trust=trustListHead;if(trust){trustListCount--;trustListHead=trust->next;}PORT_Assert(trustListCount>=0);nsslowcert_UnlockFreeList();if(trust){returntrust;}returnPORT_ZNew(NSSLOWCERTTrust);}staticvoidDestroyTrustFreeList(void){NSSLOWCERTTrust*trust;nsslowcert_LockFreeList();while(NULL!=(trust=trustListHead)){trustListCount--;trustListHead=trust->next;PORT_Free(trust);}PORT_Assert(!trustListCount);trustListCount=0;nsslowcert_UnlockFreeList();}staticNSSLOWCERTTrust*DecodeTrustEntry(NSSLOWCERTCertDBHandle*handle,certDBEntryCert*entry,constSECItem*dbKey){NSSLOWCERTTrust*trust=CreateTrust();if(trust==NULL){returntrust;}trust->dbhandle=handle;trust->dbEntry=entry;trust->dbKey.data=pkcs11_copyStaticData(dbKey->data,dbKey->len,trust->dbKeySpace,sizeof(trust->dbKeySpace));if(!trust->dbKey.data){PORT_Free(trust);returnNULL;}trust->dbKey.len=dbKey->len;trust->trust=&entry->trust;trust->derCert=&entry->derCert;return(trust);}typedefstruct{PermCertCallbackcertfunc;NSSLOWCERTCertDBHandle*handle;void*data;}PermCertCallbackState;/* * traversal callback to decode certs and call callers callback */staticSECStatuscertcallback(SECItem*dbdata,SECItem*dbkey,certDBEntryTypetype,void*data){PermCertCallbackState*mystate;SECStatusrv;certDBEntryCert*entry;SECItementryitem;NSSLOWCERTCertificate*cert;PLArenaPool*arena=NULL;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){gotoloser;}entry=(certDBEntryCert*)PORT_ArenaAlloc(arena,sizeof(certDBEntryCert));if(!entry){PORT_SetError(SEC_ERROR_NO_MEMORY);gotoloser;}mystate=(PermCertCallbackState*)data;entry->common.version=(unsignedint)dbdata->data[0];entry->common.type=(certDBEntryType)dbdata->data[1];entry->common.flags=(unsignedint)dbdata->data[2];entry->common.arena=arena;entryitem.len=dbdata->len-SEC_DB_ENTRY_HEADER_LEN;entryitem.data=&dbdata->data[SEC_DB_ENTRY_HEADER_LEN];rv=DecodeDBCertEntry(entry,&entryitem);if(rv!=SECSuccess){gotoloser;}entry->derCert.type=siBuffer;/* note: Entry is 'inheritted'. */cert=DecodeACert(mystate->handle,entry);rv=(*mystate->certfunc)(cert,dbkey,mystate->data);/* arena stored in entry destroyed by nsslowcert_DestroyCertificate */nsslowcert_DestroyCertificateNoLocking(cert);return(rv);loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}return(SECFailure);}/* * Traverse all of the certificates in the permanent database and * call the given function for each one; expect the caller to have lock. */staticSECStatusTraversePermCertsNoLocking(NSSLOWCERTCertDBHandle*handle,SECStatus(*certfunc)(NSSLOWCERTCertificate*cert,SECItem*k,void*pdata),void*udata){SECStatusrv;PermCertCallbackStatemystate;mystate.certfunc=certfunc;mystate.handle=handle;mystate.data=udata;rv=nsslowcert_TraverseDBEntries(handle,certDBEntryTypeCert,certcallback,(void*)&mystate);return(rv);}/* * Traverse all of the certificates in the permanent database and * call the given function for each one. */SECStatusnsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle*handle,SECStatus(*certfunc)(NSSLOWCERTCertificate*cert,SECItem*k,void*pdata),void*udata){SECStatusrv;nsslowcert_LockDB(handle);rv=TraversePermCertsNoLocking(handle,certfunc,udata);nsslowcert_UnlockDB(handle);return(rv);}/* * Close the database */voidnsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle*handle){if(handle){if(handle->permCertDB){certdb_Close(handle->permCertDB);handle->permCertDB=NULL;}if(handle->dbMon){PZ_DestroyMonitor(handle->dbMon);handle->dbMon=NULL;}PORT_Free(handle);}return;}/* * Get the trust attributes from a certificate */SECStatusnsslowcert_GetCertTrust(NSSLOWCERTCertificate*cert,NSSLOWCERTCertTrust*trust){SECStatusrv;nsslowcert_LockCertTrust(cert);if(cert->trust==NULL){rv=SECFailure;}else{*trust=*cert->trust;rv=SECSuccess;}nsslowcert_UnlockCertTrust(cert);return(rv);}/* * Change the trust attributes of a certificate and make them permanent * in the database. */SECStatusnsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle*handle,NSSLOWCERTCertificate*cert,NSSLOWCERTCertTrust*trust){certDBEntryCert*entry;intrv;SECStatusret;nsslowcert_LockDB(handle);nsslowcert_LockCertTrust(cert);/* only set the trust on permanent certs */if(cert->trust==NULL){ret=SECFailure;gotodone;}*cert->trust=*trust;if(cert->dbEntry==NULL){ret=SECSuccess;/* not in permanent database */gotodone;}entry=cert->dbEntry;entry->trust=*trust;rv=WriteDBCertEntry(handle,entry);if(rv){ret=SECFailure;gotodone;}ret=SECSuccess;done:nsslowcert_UnlockCertTrust(cert);nsslowcert_UnlockDB(handle);return(ret);}staticSECStatusnsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle*dbhandle,NSSLOWCERTCertificate*cert,char*nickname,NSSLOWCERTCertTrust*trust){char*oldnn;certDBEntryCert*entry;PRBoolconflict;SECStatusret;PORT_Assert(!cert->dbEntry);/* don't add a conflicting nickname */conflict=nsslowcert_CertNicknameConflict(nickname,&cert->derSubject,dbhandle);if(conflict){ret=SECFailure;gotodone;}/* save old nickname so that we can delete it */oldnn=cert->nickname;entry=AddCertToPermDB(dbhandle,cert,nickname,trust);if(entry==NULL){ret=SECFailure;gotodone;}pkcs11_freeNickname(oldnn,cert->nicknameSpace);cert->nickname=(entry->nickname)?pkcs11_copyNickname(entry->nickname,cert->nicknameSpace,sizeof(cert->nicknameSpace)):NULL;cert->trust=&entry->trust;cert->dbEntry=entry;ret=SECSuccess;done:return(ret);}SECStatusnsslowcert_AddPermCert(NSSLOWCERTCertDBHandle*dbhandle,NSSLOWCERTCertificate*cert,char*nickname,NSSLOWCERTCertTrust*trust){SECStatusret;nsslowcert_LockDB(dbhandle);ret=nsslowcert_UpdatePermCert(dbhandle,cert,nickname,trust);nsslowcert_UnlockDB(dbhandle);return(ret);}/* * Open the certificate database and index databases. Create them if * they are not there or bad. */SECStatusnsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle*handle,PRBoolreadOnly,constchar*appName,constchar*prefix,NSSLOWCERTDBNameFuncnamecb,void*cbarg,PRBoolopenVolatile){intrv;certdb_InitDBLock(handle);handle->dbMon=PZ_NewMonitor(nssILockCertDB);PORT_Assert(handle->dbMon!=NULL);handle->dbVerify=PR_FALSE;rv=nsslowcert_OpenPermCertDB(handle,readOnly,appName,prefix,namecb,cbarg);if(rv){gotoloser;}return(SECSuccess);loser:if(handle->dbMon){PZ_DestroyMonitor(handle->dbMon);handle->dbMon=NULL;}PORT_SetError(SEC_ERROR_BAD_DATABASE);return(SECFailure);}PRBoolnsslowcert_needDBVerify(NSSLOWCERTCertDBHandle*handle){if(!handle)returnPR_FALSE;returnhandle->dbVerify;}voidnsslowcert_setDBVerify(NSSLOWCERTCertDBHandle*handle,PRBoolvalue){handle->dbVerify=value;}/* * Lookup a certificate in the databases. */staticNSSLOWCERTCertificate*FindCertByKey(NSSLOWCERTCertDBHandle*handle,constSECItem*certKey,PRBoollockdb){NSSLOWCERTCertificate*cert=NULL;certDBEntryCert*entry;PRBoollocked=PR_FALSE;if(lockdb){locked=PR_TRUE;nsslowcert_LockDB(handle);}/* find in perm database */entry=ReadDBCertEntry(handle,certKey);if(entry==NULL){gotoloser;}/* inherit entry */cert=DecodeACert(handle,entry);loser:if(cert==NULL){if(entry){DestroyDBEntry((certDBEntry*)entry);}}if(locked){nsslowcert_UnlockDB(handle);}return(cert);}/* * Lookup a certificate in the databases. */staticNSSLOWCERTTrust*FindTrustByKey(NSSLOWCERTCertDBHandle*handle,constSECItem*certKey,PRBoollockdb){NSSLOWCERTTrust*trust=NULL;certDBEntryCert*entry;PRBoollocked=PR_FALSE;if(lockdb){locked=PR_TRUE;nsslowcert_LockDB(handle);}/* find in perm database */entry=ReadDBCertEntry(handle,certKey);if(entry==NULL){gotoloser;}if(!nsslowcert_hasTrust(&entry->trust)){gotoloser;}/* inherit entry */trust=DecodeTrustEntry(handle,entry,certKey);loser:if(trust==NULL){if(entry){DestroyDBEntry((certDBEntry*)entry);}}if(locked){nsslowcert_UnlockDB(handle);}return(trust);}/* * Lookup a certificate in the databases without locking */NSSLOWCERTCertificate*nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle*handle,constSECItem*certKey){return(FindCertByKey(handle,certKey,PR_FALSE));}/* * Lookup a trust object in the databases without locking */NSSLOWCERTTrust*nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle*handle,constSECItem*certKey){return(FindTrustByKey(handle,certKey,PR_FALSE));}/* * Generate a key from an issuerAndSerialNumber, and find the * associated cert in the database. */NSSLOWCERTCertificate*nsslowcert_FindCertByIssuerAndSN(NSSLOWCERTCertDBHandle*handle,NSSLOWCERTIssuerAndSN*issuerAndSN){SECItemcertKey;SECItem*sn=&issuerAndSN->serialNumber;SECItem*issuer=&issuerAndSN->derIssuer;NSSLOWCERTCertificate*cert;intdata_len=sn->len;intindex=0;/* automatically detect DER encoded serial numbers and remove the der * encoding since the database expects unencoded data. * if it's DER encoded, there must be at least 3 bytes, tag, len, data */if((sn->len>=3)&&(sn->data[0]==0x2)){/* remove the der encoding of the serial number before generating the * key.. */intdata_left=sn->len-2;data_len=sn->data[1];index=2;/* extended length ? (not very likely for a serial number) */if(data_len&0x80){intlen_count=data_len&0x7f;data_len=0;data_left-=len_count;if(data_left>0){while(len_count--){data_len=(data_len<<8)|sn->data[index++];}}}/* XXX leaving any leading zeros on the serial number for backwards * compatibility *//* not a valid der, must be just an unlucky serial number value */if(data_len!=data_left){data_len=sn->len;index=0;}}certKey.type=0;certKey.data=(unsignedchar*)PORT_Alloc(sn->len+issuer->len);certKey.len=data_len+issuer->len;if(certKey.data==NULL){return(0);}/* first try the serial number as hand-decoded above*//* copy the serialNumber */PORT_Memcpy(certKey.data,&sn->data[index],data_len);/* copy the issuer */PORT_Memcpy(&certKey.data[data_len],issuer->data,issuer->len);cert=nsslowcert_FindCertByKey(handle,&certKey);if(cert){PORT_Free(certKey.data);return(cert);}/* didn't find it, try by der encoded serial number *//* copy the serialNumber */PORT_Memcpy(certKey.data,sn->data,sn->len);/* copy the issuer */PORT_Memcpy(&certKey.data[sn->len],issuer->data,issuer->len);certKey.len=sn->len+issuer->len;cert=nsslowcert_FindCertByKey(handle,&certKey);PORT_Free(certKey.data);return(cert);}/* * Generate a key from an issuerAndSerialNumber, and find the * associated cert in the database. */NSSLOWCERTTrust*nsslowcert_FindTrustByIssuerAndSN(NSSLOWCERTCertDBHandle*handle,NSSLOWCERTIssuerAndSN*issuerAndSN){SECItemcertKey;SECItem*sn=&issuerAndSN->serialNumber;SECItem*issuer=&issuerAndSN->derIssuer;NSSLOWCERTTrust*trust;unsignedcharkeyBuf[512];intdata_len=sn->len;intindex=0;intlen;/* automatically detect DER encoded serial numbers and remove the der * encoding since the database expects unencoded data. * if it's DER encoded, there must be at least 3 bytes, tag, len, data */if((sn->len>=3)&&(sn->data[0]==0x2)){/* remove the der encoding of the serial number before generating the * key.. */intdata_left=sn->len-2;data_len=sn->data[1];index=2;/* extended length ? (not very likely for a serial number) */if(data_len&0x80){intlen_count=data_len&0x7f;data_len=0;data_left-=len_count;if(data_left>0){while(len_count--){data_len=(data_len<<8)|sn->data[index++];}}}/* XXX leaving any leading zeros on the serial number for backwards * compatibility *//* not a valid der, must be just an unlucky serial number value */if(data_len!=data_left){data_len=sn->len;index=0;}}certKey.type=0;certKey.len=data_len+issuer->len;len=sn->len+issuer->len;if(len>sizeof(keyBuf)){certKey.data=(unsignedchar*)PORT_Alloc(len);}else{certKey.data=keyBuf;}if(certKey.data==NULL){return(0);}/* first try the serial number as hand-decoded above*//* copy the serialNumber */PORT_Memcpy(certKey.data,&sn->data[index],data_len);/* copy the issuer */PORT_Memcpy(&certKey.data[data_len],issuer->data,issuer->len);trust=nsslowcert_FindTrustByKey(handle,&certKey);if(trust){pkcs11_freeStaticData(certKey.data,keyBuf);return(trust);}if(index==0){pkcs11_freeStaticData(certKey.data,keyBuf);returnNULL;}/* didn't find it, try by der encoded serial number *//* copy the serialNumber */PORT_Memcpy(certKey.data,sn->data,sn->len);/* copy the issuer */PORT_Memcpy(&certKey.data[sn->len],issuer->data,issuer->len);certKey.len=sn->len+issuer->len;trust=nsslowcert_FindTrustByKey(handle,&certKey);pkcs11_freeStaticData(certKey.data,keyBuf);return(trust);}/* * look for the given DER certificate in the database */NSSLOWCERTCertificate*nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle*handle,SECItem*derCert){PLArenaPool*arena;SECItemcertKey;SECStatusrv;NSSLOWCERTCertificate*cert=NULL;/* create a scratch arena */arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){return(NULL);}/* extract the database key from the cert */rv=nsslowcert_KeyFromDERCert(arena,derCert,&certKey);if(rv!=SECSuccess){gotoloser;}/* find the certificate */cert=nsslowcert_FindCertByKey(handle,&certKey);loser:PORT_FreeArena(arena,PR_FALSE);return(cert);}staticvoidDestroyCertificate(NSSLOWCERTCertificate*cert,PRBoollockdb){intrefCount;NSSLOWCERTCertDBHandle*handle;if(cert){handle=cert->dbhandle;/* * handle may be NULL, for example if the cert was created with * nsslowcert_DecodeDERCertificate. */if(lockdb&&handle){nsslowcert_LockDB(handle);}nsslowcert_LockCertRefCount(cert);PORT_Assert(cert->referenceCount>0);refCount=--cert->referenceCount;nsslowcert_UnlockCertRefCount(cert);if(refCount==0){certDBEntryCert*entry=cert->dbEntry;if(entry){DestroyDBEntry((certDBEntry*)entry);}pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);pkcs11_freeNickname(cert->emailAddr,cert->emailAddrSpace);pkcs11_freeStaticData(cert->certKey.data,cert->certKeySpace);cert->certKey.data=NULL;cert->nickname=NULL;/* zero cert before freeing. Any stale references to this cert * after this point will probably cause an exception. */PORT_Memset(cert,0,sizeof*cert);/* use reflock to protect the free list */nsslowcert_LockFreeList();if(certListCount>MAX_CERT_LIST_COUNT){PORT_Free(cert);}else{certListCount++;cert->next=certListHead;certListHead=cert;}nsslowcert_UnlockFreeList();cert=NULL;}if(lockdb&&handle){nsslowcert_UnlockDB(handle);}}return;}NSSLOWCERTCertificate*nsslowcert_CreateCert(void){NSSLOWCERTCertificate*cert;nsslowcert_LockFreeList();cert=certListHead;if(cert){certListHead=cert->next;certListCount--;}PORT_Assert(certListCount>=0);nsslowcert_UnlockFreeList();if(cert){returncert;}returnPORT_ZNew(NSSLOWCERTCertificate);}staticvoidDestroyCertFreeList(void){NSSLOWCERTCertificate*cert;nsslowcert_LockFreeList();while(NULL!=(cert=certListHead)){certListCount--;certListHead=cert->next;PORT_Free(cert);}PORT_Assert(!certListCount);certListCount=0;nsslowcert_UnlockFreeList();}voidnsslowcert_DestroyTrust(NSSLOWCERTTrust*trust){certDBEntryCert*entry=trust->dbEntry;if(entry){DestroyDBEntry((certDBEntry*)entry);}pkcs11_freeStaticData(trust->dbKey.data,trust->dbKeySpace);PORT_Memset(trust,0,sizeof(*trust));nsslowcert_LockFreeList();if(trustListCount>MAX_TRUST_LIST_COUNT){PORT_Free(trust);}else{trustListCount++;trust->next=trustListHead;trustListHead=trust;}nsslowcert_UnlockFreeList();return;}voidnsslowcert_DestroyCertificate(NSSLOWCERTCertificate*cert){DestroyCertificate(cert,PR_TRUE);return;}staticvoidnsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate*cert){DestroyCertificate(cert,PR_FALSE);return;}/* * Lookup a CRL in the databases. We mirror the same fast caching data base * caching stuff used by certificates....? */certDBEntryRevocation*nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle*handle,SECItem*crlKey,PRBoolisKRL){SECItemkeyitem;SECStatusrv;PLArenaPool*arena=NULL;certDBEntryRevocation*entry=NULL;certDBEntryTypecrlType=isKRL?certDBEntryTypeKeyRevocation:certDBEntryTypeRevocation;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){gotoloser;}rv=EncodeDBGenericKey(crlKey,arena,&keyitem,crlType);if(rv!=SECSuccess){gotoloser;}/* find in perm database */entry=ReadDBCrlEntry(handle,crlKey,crlType);if(entry==NULL){gotoloser;}loser:if(arena){PORT_FreeArena(arena,PR_FALSE);}returnentry;}/* * replace the existing URL in the data base with a new one */staticSECStatusnsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle*handle,SECItem*derCrl,SECItem*crlKey,char*url,PRBoolisKRL){SECStatusrv=SECFailure;certDBEntryRevocation*entry=NULL;certDBEntryTypecrlType=isKRL?certDBEntryTypeKeyRevocation:certDBEntryTypeRevocation;DeleteDBCrlEntry(handle,crlKey,crlType);/* Write the new entry into the data base */entry=NewDBCrlEntry(derCrl,url,crlType,0);if(entry==NULL)gotodone;rv=WriteDBCrlEntry(handle,entry,crlKey);if(rv!=SECSuccess)gotodone;done:if(entry){DestroyDBEntry((certDBEntry*)entry);}returnrv;}SECStatusnsslowcert_AddCrl(NSSLOWCERTCertDBHandle*handle,SECItem*derCrl,SECItem*crlKey,char*url,PRBoolisKRL){SECStatusrv;rv=nsslowcert_UpdateCrl(handle,derCrl,crlKey,url,isKRL);returnrv;}SECStatusnsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle*handle,constSECItem*derName,PRBoolisKRL){SECStatusrv;certDBEntryTypecrlType=isKRL?certDBEntryTypeKeyRevocation:certDBEntryTypeRevocation;rv=DeleteDBCrlEntry(handle,derName,crlType);if(rv!=SECSuccess)gotodone;done:returnrv;}PRBoolnsslowcert_hasTrust(NSSLOWCERTCertTrust*trust){if(trust==NULL){returnPR_FALSE;}return!((trust->sslFlags&CERTDB_TRUSTED_UNKNOWN)&&(trust->emailFlags&CERTDB_TRUSTED_UNKNOWN)&&(trust->objectSigningFlags&CERTDB_TRUSTED_UNKNOWN));}/* * This function has the logic that decides if another person's cert and * email profile from an S/MIME message should be saved. It can deal with * the case when there is no profile. */staticSECStatusnsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle*dbhandle,char*emailAddr,SECItem*derSubject,SECItem*emailProfile,SECItem*profileTime){certDBEntrySMime*entry=NULL;SECStatusrv=SECFailure;;/* find our existing entry */entry=nsslowcert_ReadDBSMimeEntry(dbhandle,emailAddr);if(entry){/* keep our old db entry consistant for old applications. */if(!SECITEM_ItemsAreEqual(derSubject,&entry->subjectName)){nsslowcert_UpdateSubjectEmailAddr(dbhandle,&entry->subjectName,emailAddr,nsslowcert_remove);}DestroyDBEntry((certDBEntry*)entry);entry=NULL;}/* now save the entry */entry=NewDBSMimeEntry(emailAddr,derSubject,emailProfile,profileTime,0);if(entry==NULL){rv=SECFailure;gotoloser;}nsslowcert_LockDB(dbhandle);rv=DeleteDBSMimeEntry(dbhandle,emailAddr);/* if delete fails, try to write new entry anyway... *//* link subject entry back here */rv=nsslowcert_UpdateSubjectEmailAddr(dbhandle,derSubject,emailAddr,nsslowcert_add);if(rv!=SECSuccess){nsslowcert_UnlockDB(dbhandle);gotoloser;}rv=WriteDBSMimeEntry(dbhandle,entry);if(rv!=SECSuccess){nsslowcert_UnlockDB(dbhandle);gotoloser;}nsslowcert_UnlockDB(dbhandle);rv=SECSuccess;loser:if(entry){DestroyDBEntry((certDBEntry*)entry);}return(rv);}SECStatusnsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle*dbhandle,char*emailAddr,SECItem*derSubject,SECItem*emailProfile,SECItem*profileTime){SECStatusrv=SECFailure;;rv=nsslowcert_UpdateSMimeProfile(dbhandle,emailAddr,derSubject,emailProfile,profileTime);return(rv);}voidnsslowcert_DestroyFreeLists(void){if(freeListLock==NULL){return;}DestroyCertEntryFreeList();DestroyTrustFreeList();DestroyCertFreeList();SKIP_AFTER_FORK(PZ_DestroyLock(freeListLock));freeListLock=NULL;}voidnsslowcert_DestroyGlobalLocks(void){if(dbLock){SKIP_AFTER_FORK(PZ_DestroyLock(dbLock));dbLock=NULL;}if(certRefCountLock){SKIP_AFTER_FORK(PZ_DestroyLock(certRefCountLock));certRefCountLock=NULL;}if(certTrustLock){SKIP_AFTER_FORK(PZ_DestroyLock(certTrustLock));certTrustLock=NULL;}}certDBEntry*nsslowcert_DecodeAnyDBEntry(SECItem*dbData,constSECItem*dbKey,certDBEntryTypeentryType,void*pdata){PLArenaPool*arena=NULL;certDBEntry*entry;SECStatusrv;SECItemdbEntry;if((dbData->len<SEC_DB_ENTRY_HEADER_LEN)||(dbKey->len==0)){PORT_SetError(SEC_ERROR_INVALID_ARGS);gotoloser;}dbEntry.data=&dbData->data[SEC_DB_ENTRY_HEADER_LEN];dbEntry.len=dbData->len-SEC_DB_ENTRY_HEADER_LEN;arena=PORT_NewArena(DER_DEFAULT_CHUNKSIZE);if(arena==NULL){gotoloser;}entry=PORT_ArenaZNew(arena,certDBEntry);if(!entry)gotoloser;entry->common.version=(unsignedint)dbData->data[0];entry->common.flags=(unsignedint)dbData->data[2];entry->common.type=entryType;entry->common.arena=arena;switch(entryType){casecertDBEntryTypeContentVersion:/* This type appears to be unused */casecertDBEntryTypeVersion:/* This type has only the common hdr */rv=SECSuccess;break;casecertDBEntryTypeSubject:rv=DecodeDBSubjectEntry(&entry->subject,&dbEntry,dbKey);break;casecertDBEntryTypeNickname:rv=DecodeDBNicknameEntry(&entry->nickname,&dbEntry,(char*)dbKey->data);break;/* smime profiles need entries created after the certs have * been imported, loop over them in a second run */casecertDBEntryTypeSMimeProfile:rv=DecodeDBSMimeEntry(&entry->smime,&dbEntry,(char*)dbKey->data);break;casecertDBEntryTypeCert:rv=DecodeDBCertEntry(&entry->cert,&dbEntry);break;casecertDBEntryTypeKeyRevocation:casecertDBEntryTypeRevocation:rv=DecodeDBCrlEntry(&entry->revocation,&dbEntry);break;default:PORT_SetError(SEC_ERROR_INVALID_ARGS);rv=SECFailure;}if(rv==SECSuccess)returnentry;loser:if(arena)PORT_FreeArena(arena,PR_FALSE);returnNULL;}